Skip to content

Instantly share code, notes, and snippets.

@orthecreedence
Last active August 29, 2015 14:02
Show Gist options
  • Save orthecreedence/2e7d974cfbbed733347f to your computer and use it in GitHub Desktop.
Save orthecreedence/2e7d974cfbbed733347f to your computer and use it in GitHub Desktop.
Non-recursive macrolet
(defpackage :mlet-nonr
(:use :cl))
(in-package :mlet-nonr)
(defun %attack (item fn)
(format t "attacking ~a, then calling fn~%" (car item))
(funcall fn (car item)))
(defmacro attack (item fn)
"This is a macro so we can bind to multiple values. In this case, %attack only
uses the first value, but this is just for brevity. Attack *must* be a macro
for my purposes."
`(let ((vals (multiple-value-list ,item)))
(%attack vals ,fn)))
(defmacro wrap (&body body &environment env)
"Wraps both the `item` and `fn` of any `attack` statement in the body with a
1+. The purpose is to show that we can use a macrolet to replace the meaning
of another macro additively without devlving into infinite recursion.
We do this by grabbing the `attack` macro function directly using the env
from one level up."
`(macrolet ((attack (item fn &environment ml-env)
(funcall (macro-function 'attack ,env)
`(attack
(1+ ,item)
(lambda (w)
(1+ (funcall ,fn w))))
ml-env)))
,@body))
;; this is the main call demonstrating the functionality. its desired
;; output:
;;
;; attacking 4, then calling fn
;; attacked.
;; => 6
(wrap
(wrap
(attack 2 (lambda (x) (format t "attacked.~%") x))))
;; the above should expand to:
(let ((vals (multiple-value-list (1+ (1+ 2)))))
(%attack vals
(lambda (w)
(1+ (funcall
(lambda (w)
(1+
(funcall
(lambda (x) (format t "attacked.~%") x)
w)))
w)))))
@orthecreedence
Copy link
Author

Note that this works great in just about every impl I've tested except ECL, which is my target currently.

It fails with:

Condition of type: SIMPLE-PROGRAM-ERROR
In form
(#1=((:BLOCK MLET-NONR::TEST ...) (SI:FUNCTION-BOUNDARY SPECIAL ...)))
FUNCTION: Not a valid argument #1#.

@orthecreedence
Copy link
Author

Note...the fix was to quote env:

(funcall (macro-function 'attack ,env)

becomes:

(funcall (macro-function 'attack ',env)

Thanks, Bike/pjb on #lisp =]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment