Haskell Kernels and Scheme

I learned recently about a concept in Haskell called a kernel. Haskell is comprised of a number of syntactic conveniences that can be translated into a base syntax — a modified version of the lambda calculus. Learning about these kernels has helped me write more useful macros in Scheme, a sparse language that is also based on the lambda calculus.

The for macro below is inspired by list comprehensions in Haskell wherein every element of one list is applied to every element of n number of lists if an optional predicate is satisfied. Within the Haskell kernel, a list comprehension is revealed to be syntactic sugar over a monadic instance. The monad, in this case, is nondeterminism, where all possible data combinations are expressed as a list.

;; === macro ===
(define-syntax for
  (syntax-rules (<- when)
    ;; base case
    [(_ ([x <- mx]) expression)
    (concat-map mx (lambda (x)
                    (list expression)))]
    ;; recursive case
    [(_ ([x <- mx] [y <- my] ...) expression)
    (concat-map mx (lambda (x)
                    (for ([y <- my] ...) expression)))]
    ;; base case with predicate
    [(_ ([x <- mx]) (when predicate) expression)
    (concat-map mx (lambda (x)
                    (if predicate
                        (list expression)
                        empty)))]
    ;; recursive case with predicate
    [(_ ([x <- mx] [y <- my] ...) (when predicate) expression)
    (concat-map mx (lambda (x)
                    (for ([y <- my] ...) (when predicate) expression)))]))

;; === monad ===

(define empty '())

(define concat-map
  (lambda (xs fn)
    (if (null? xs)
        empty
        (append (fn (car xs)) (concat-map (cdr xs) fn)))))

;; === example ===

(define binary '(0 1))

(define count-to-fifteen
  (for ([bit-4 <- binary]
        [bit-3 <- binary]
        [bit-2 <- binary]
        [bit-1 <- binary])
    (list bit-4 bit-3 bit-2 bit-1)))

;; - evaluates ->

'((0 0 0 0) (0 0 0 1) (0 0 1 0) (0 0 1 1)
  (0 1 0 0) (0 1 0 1) (0 1 1 0) (0 1 1 1)
  (1 0 0 0) (1 0 0 1) (1 0 1 0) (1 0 1 1)
  (1 1 0 0) (1 1 0 1) (1 1 1 0) (1 1 1 1)))
A Scheme macro that defines a Haskell-like list comprehension.