Monolune

Using Racket for The Reasoned Schemer

I started reading The Reasoned Schemer last week, and wanted to try out some of the exercises in the book, as well as to conduct logic programming experiments of my own. Racket is now my language of choice for experiments, so I thought that it would be good to do the exercises in The Reasoned Schemer using Racket instead of Scheme. This article is about setting up Racket with miniKanren to be able to follow the material presented in the book.

The first problem I encountered was the need to find a working miniKanren library to use. There are two packages one can use. Installation is simple for both of them.

The first option is raco pkg install minikanren. Now to use miniKanren, all that is needed is a (require minikanren) right after the #lang racket line of the program. If you're curious about the contents of this package, its documentation can be viewed using the usual Racket documentation system (i.e. raco docs).

So far, all is well, but the minikanren package does not define succeed and fail, which we really need for following the book. In addition, the meaning of else used in the book is different from the vanilla elseused in normal Scheme. To define these missing pieces:

#lang racket
(require minikanren)
(define succeed (== #t #t))
(define fail (== #t #f))
(define else succeed)

; Your code here ...

An alternative miniKanren package is the one provided by the authors, which is the canonical implementation of miniKanren in Racket (Racket-miniKanren). Installation is straightforward, but there is little documentation. The package has to be obtained from GitHub instead of through the Racket Package Index. To install this package: raco pkg install github://github.com/miniKanren/Racket-miniKanren/master. To use the package, (require Racket-miniKanren/miniKanren/mk) is needed after the #lang racket line. I use this package because it is the canonical implementation.

Both of these miniKanren packages do not define #s and #u, which are used throughout the book. If you would like to be able to use #s and #u as shorter versions of succeed and fail, you can add these lines right before your code:

; Define #s.
(current-readtable
  (make-readtable (current-readtable)
                  #s
                  'dispatch-macro
                  (lambda (a b c d e f) succeed)))

; Define #u.
(current-readtable
  (make-readtable (current-readtable)
                  #u
                  'dispatch-macro
                  (lambda (a b c d e f) fail)))

Note: this method of defining #s and #u looks hacky, so if you know a better way, do let me know in the comments.

That's all. Whichever package you choose, everything in the book should go smoothly. Happy learning and experimenting!