/scheme

Exercism exercises in Scheme.

Primary LanguageSchemeMIT LicenseMIT

Exercism Scheme Track

[![Configlet Status](https://github.com/exercism/scheme/workflows/configlet/badge.svg)] [![Exercise Test Status](https://github.com/exercism/scheme/workflows/scheme%20%2F%20main/badge.svg)]

Exercisms in scheme.

Overview

Welcome to the home of the exercism scheme track! Many parts of the track are specified inside scheme and then output in various formats. For example, exercism wants a config.json file, which is actually specified in config/track.ss. Everything including the exercise implementations, and the documentation is also done from scheme. In general, the way to modify the track is to edit something in the input/ directory. Exercism problem implementations live in input/exercises/. Student facing documentation (like resources and links) is built from files in input/docs/.

Setup

To work on the scheme track you’ll need ChezScheme and GNU Guile. The goal is for exercises to be implemented such that they work in either scheme. Each exercise has tests and an example solution and, as part of the build process, it is checked that these tests pass in both schemes.

There is a small json library (borrowed from packrat) in code/json.sls, which relies on some modules from the srfi collection. ChezScheme finds libraries through an environment variable CHEZSCHEMELIBDIRS. So, you’ll need to make sure the location of your downloaded srfi is added to CHEZSCHEMELIBDIRS. Instructions for installing the srfi collection can be found at srfi/INSTALL.chez. If you’re using either the guix or nix package managers, you can find the srfis pre-packaged.

Working on the Scheme track

To work the track, you should start by heading to this directory and firing up a Scheme REPL. Enter (load "load.ss"), which orchestrates loading the appropriate files.

Exercises are typically specified in problem-specifications, a repository that has descriptions, versions, and tests described through JSON files. The Makefile will clone it; the scheme in code/ will read problem information from that JSON. It is, however, also possible to define novel exercises if you wish.

Requirements

Exercises have a few required parts, taking the exercise input/exercises/pascals-triangle/ as an example.

  • The example.scm file which is an example solution that must pass the tests. It must also use only r6rs scheme features so that it can run in both Guile and Chez scheme.
  • A stub file pascals-triangle.scm usually named after the problem that the student will be given to write their solultion.
  • The test.ss which builds the test cases as well as problem metadata (such as problem versions, read from the specifications) and extra documentation (if needed). The crucial part of this file is a call to put-problem!, a procedure defined in code/track.ss that defines the problem whose name is a symbol (eg 'pascals-triangle) and an association list with the following form
(let ((spec (get-test-specification 'pascals-triangle)))
  (put-problem!
   'pascals-triangle
   `((test . ,(spec->tests spec))
     (stubs pascals-triangle)
     (version . ,(lookup 'version spec))
     (skeleton . "pascals-triangle.scm")
     (solution . "example.scm")
     (markdown . ,(splice-exercism 'pascals-triangle)))))

The tests are parsed in a fairly horrible and ad-hoc way:

(define (spec->tests spec)
  (map parse-test
       (lookup 'cases
               (car
                (lookup 'cases spec)))))

due to the fairly irregular problem specifications. parse-test is defined:

(define (parse-test test)
  `(test-success ,(lookup 'description test)
                 equal?
                 pascals-triangle
                 '(,(cdar (lookup 'input test)))
                 ',(lookup 'expected test)))

pascals-triangle has a fairly friendly specification. An example of a less straightforward one from change is the following:

(define (parse-test test)
  (let ((expected (lookup 'expected test))
        (input (lookup 'input test)))
    (if (or (null? expected)
            (number? (car expected)))
        `(test-success ,(lookup 'description test)
                       (lambda (out expected)
                         (equal? (list-sort < out) (list-sort < expected)))
                       change
                       '(,(lookup 'target input)
                         ,(lookup 'coins input))
                       ',expected)
        `(test-error ,(lookup 'description test)
                     change
                     '(,(lookup 'target input)
                       ,(lookup 'coins input))))))

There are test cases expected to fail and a more elaborate passing predicate to allow students to return answers in any order.

Final requirement:

  • Add the problem to the configuration expression in config/track.ss. You need to provide a uuid, which can be generated from the scheme repl by calling (configlet-uuid) (which is just a wrapper for the configlet binary). Finally, include the problem in the list of implementations in the Makefile.

When building the track, the makefile reads each exercise directory and uses the test.ss files to generate the test.scm files that the students use. The procedure spec->tests reads the parsed specification json and turns that into runnable scheme tests. The json is parsed through a procedure get-test-specification.

Basically, test.ss defines each problem as an instance of an exercism problem, having the parts described above, which are used to output the files that the students get.

Extras

Helpful and useful procedures include:

  • To start a new exercise, use (stub-exercism 'change). This creates rough stubs for test.ss, change.scm, and example.scm in the directory input/exercises/change/.
  • (verify-exercism 'change) checks that the solution passes generated test suite. It ought to work under both Guile and Chez, with (rnrs) as the imported library.

Adding problems not in exercism/problem-specifications

New problems from outside of the specifications are welcome as well. Develop them as described above in the code/exercises directory, following pretty much the same process as above. The main difference is that instead of parsing test cases from the problem-specifications repository, you’ll write them by hand in a test.ss file.

Scheme icon

The Scheme logo was created by https://en.wikipedia.org/wiki/User:Matthias.f and released under the Creative Commons Attribution-Share Alike 3.0 Unported license. We adapted the logo, creating a pink/black version to use on Exercism.