Evil Scheme

2022-12-13

Because Scheme and other languages in the Lisp family are highly flexible, they can incorporate many peculiar features. Even writing Scheme code in an imperative style like Python is possible.

The code in this article is written for GNU Guile and TinyScheme, both of which support traditional Lisp macros. These macros are more commonly found in Common Lisp, not considered hygienic, and do not belong to any Scheme standard. However, most interpreters and compilers support them.

For example, if you want to run the code in Racket, simply include the following syntax at the beginning of the code:

#lang racket

(define-syntax define-macro
  (lambda (x)
    (syntax-case x ()
      ((_ (macro . args) body ...)
       #'(define-macro macro (lambda args body ...)))
      ((_ macro transformer)
       #'(define-syntax macro
           (lambda (y)
             (syntax-case y ()
               ((_ . args)
                (let ((v (syntax->datum #'args)))
                  (datum->syntax y (apply transformer v)))))))))))

hello, world

Python code:

print("hello, world")

Function definition:

(define println
    (lambda x
      (apply display x)
      (newline)))

Result:

(println "hello world")

Def

Python code:

def is_even(x):
    if x % 2 == 0:
        return True
    return False

Macro definition:

(define-macro (def form . body)
    `(define ,form
         (call/cc (lambda (return)
            ,@body))))

Result:

(def (is-even x)
    (cond ((= 0 (modulo x 2))
        (return #t)))
    (return #f))

While

Python code:

i = 0
while True:
    i = i + 1
    if x % 2 == 0:
        continue
    print(i)
    if i > 10:
        break

Macro definition:

(define-macro (while condition . body)
    (let ((loop (gensym)))
        `(call/cc (lambda (break)
            (letrec ((,loop (lambda ()
                (cond (,condition
                    (call/cc (lambda (continue)
                            ,@body))
                    (,loop))))))
                (,loop))))))

Result:

(let ((i 0))
(while #t
    (set! i (+ i 1))
    (cond ((= (modulo i 2) 0)
         (continue)))
    (cond ((> i 10)
        (break)))
    (println i)))

For

Python code:

for i in range(0, 10):
    print(i)

Macro and util functions:

(define (iter-get iter)
    (cond ((list? iter)
        (car iter))
    (else
        (iter 'get))))

(define (iter-next iter)
    (cond ((list? iter)
        (cdr iter))
    (else
        (iter 'next))))

(define (range start end)
    (lambda (method)
        (cond ((eq? 'get method)
            (if (>= start end)
                '()
                start))
        ((eq? 'next method)
            (range (+ 1 start) end)))))

(define-macro (for i range . body)
    (let ((loop (gensym))
          (iter (gensym)))
    `(call/cc (lambda (break)
        (letrec
            ((,loop (lambda (,iter)
                (if (eq? (iter-get ,iter) '())
                    '()
                    (let ((,i (iter-get ,iter)))
                          (call/cc (lambda (continue)
                             ,@body))
                          (,loop (iter-next ,iter)))))))
            (,loop ,range))))))

Result:

(for i (range 0 10)
    (println i))

Goto

Let's forget about this!

Fizz Buzz!

Python code looks like this:

for i in range(35):
    if i % 15 == 0:
        print("FizzBuzz")
        continue
    if i % 3 == 0:
        print("Fizz")
        continue
    if i % 5 == 0:
        print("Buzz")
        continue
    print(i)

With Scheme, adding the macros above, it's almost transliterate:

(for i (range 1 35)
    (cond ((= 0 (modulo i 15))
        (println "FizzBuzz")
        (continue)))
    (cond ((= 0 (modulo i 3))
        (println "Fizz")
        (continue)))
    (cond ((= 0 (modulo i 5))
        (println "Buzz")
        (continue)))
    (println i))


Email: i (at) mistivia (dot) com