d6caf093bdc9 — Linus Björnstam 4 years ago
initial commit
2 files changed, 130 insertions(+), 0 deletions(-)

A => README.md
A => syntax/def.scm
A => README.md +23 -0
@@ 0,0 1,23 @@ 
+A small utility macro to allow defines in expression context in the "toplevel" of function bodies.
+Install it in your site-dir and import it using (use-modules (syntax def))
+
+## Example
+
+    (use-modules (syntax def))
+    (def (divide-minus-one a b)
+      (when (= b 1) (error "We don't allow that here"))
+      (define b* (- b 1))
+      (/ a b*))
+
+This transforms all defines in expression context to (letrec ...). It also supports using (def ...) in expression
+context, but those are transformed into let*, which has less overhead in guile (2.2 at least).
+
+## Portability
+The macros are currently written in syntax-rules. If that isn't supported in your scheme, then I'm not really sure
+it is a scheme.
+
+## License
+Permissified ISC.
+
+## DOCUMENTATION
+There is none, however there are is info in the source comments.

          
A => syntax/def.scm +107 -0
@@ 0,0 1,107 @@ 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Copyright 2019 Linus Björnstam
+;;
+;; Permission to use, copy, modify, and/or distribute this software for any
+;; purpose with or without fee is hereby granted, provided that the above
+;; copyright notice and this permission notice appear in all source copies.
+;; The software is provided "as is", without any express or implied warranties.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; A small extension to define* that allows for arbitrary positions of defines in
+;; the toplevel of a function body.  Guile follows r6rs in that it does not allow
+;; defines in "expression context", which means all (define ...) must happen at
+;; the top of a funciton definition. This macro solves that, in that it allows defines
+;; at any "toplevel" place within the function body.
+;;
+;; Example:
+;; in guile the following is not allowed:
+;;
+;;   (define (do-stuff a b)
+;;     (define c (wowza a))
+;;     (unless c (error "holy hell"))
+;;     (define abc (+ a b c))
+;;     (/ 100 abc))
+;;
+;; because (define abc ...) is in expression context.
+;; You solve this by using let, let*, letrec or letrec* as appropriate.
+;; OR by using this macro.
+;; The above code would work just fine if you replaced the top define with def.
+;; It transforms defines in expression context to letrec, and as an added bonus
+;; transforms (def ...) in expression context to (let* ...). Why? Because
+;; it is less heavy than letrec. If you have a function that is called a million times
+;; that has very little overhead, you will actually have quite a speedup by not using
+;; defines in the function body, at least on guile 2.2.
+;;
+;;; How does it work?
+;; Well, I'm glad you asked! The magic macro is %body, which walks through the body of
+;; def. It if finds a define, it dispatches that part of the body to %define.
+;; %define takes all defines it can find in a row and turns them into a letrec.
+;;
+;; If %body finds a (def ...) it turns it into a simple (let ...). Guile transforms
+;; any chained lets into let*.
+;;
+;;; What are the finer details?
+;; It does not recursively walk the body, so only "local toplevel" defines/defs are
+;; transformed. Doing that would be non-trivial using syntax-rules and quite simple 
+;; using syntax-case. I spent a total of 5 minutes writing this macro, and getting the
+;; inns and outs of recursive body transversal correct would have taken some more time.
+;;
+;; (def (name formals ...) body ...) gets transformed into (define* ...). This is a fine
+;; transformation to do, since any define* that does not use the extra features is the same
+;; as a regular define in every way. Internal (define (...) ...) are converted to lambda*.
+;; This is fine for the same reason.
+;;
+;;; Is it well tested?
+;; I wrote this code in 6 minutes, and it worked as I expected on the first try. I did not
+;; do any testing whatsoever. It might actually not work at all for you.
+;;
+;;; Future of this macro?
+;; I will probably convert it to syntax-case to do proper transversals and rewriting of
+;; function bodies. I want to allow for arbitrary placement of defines and defs, like
+;; for example in cond clauses.
+
+(define-module (syntax def)
+  #:export (def))
+
+(define-syntax %define
+  (syntax-rules (define)
+    ((_ (bindings ...) (define (name formals ...) body ...) rest ...)
+     (%define (bindings ... (name (%lambda (formals ...) body ...))) rest ...))
+    ((_ (bindings ...) (define name expr) rest ...)
+     (%define (bindings ... (name expr)) rest ...))
+    ((_ bindings rest ...)
+     (letrec bindings
+       (%body rest ...)))))
+
+
+(define-syntax %lambda
+  (syntax-rules ()
+    ((_ (. formals) body ...)
+     (lambda* formals body ...))
+    ((_ (formals ...) body ...)
+     (lambda* (formals ...) body ...))))
+
+(define-syntax %body
+  (syntax-rules (def define)
+    ((_ (define stuff ...) rest ...)
+     (%define () (define stuff ...) rest ...))
+    ((_ (def (name formals ...) body ...) rest ...)
+     (let ((name (%lambda (formals ...) (%body body ...))))
+       (%body rest ...)))
+    ((_ (def name expr) rest ...)
+     (let ((name expr))
+       (%body rest ...)))
+
+    ;; Just a simple expression and nothing more.
+    ;; %body is now unnecessary and it can die.
+    ((_ end)
+     end)
+    ;; A non-def(ine) expression. Examine the rest!
+    ((_ expr rest ...)
+     (begin expr (%body rest ...)))))
+
+(define-syntax def
+  (syntax-rules ()
+    ((_ name expr) (define name expr))
+    ((_ (name formals ...) body ...)
+     (define* (name formals ...) (%body body ...)))))