-
Notifications
You must be signed in to change notification settings - Fork 0
macros
There is now some very basic macro-like support implemented in the lambda conversion step (where we go from the abstract syntax tree to a lambda representation). It isn't a true macro facility, in fact what I'm calling "macros" are really just lazy functions. For example in the preamble we now have:
macro AND(a, b) { if (a) { b } else { false } }
infix left 30 "and" AND;
And similarly for the other logical operators. Note this preserves
the short circuiting of the and, e.g.
let
fn a() { print "a called"; false }
fn b() { print "b called"; true }
fn c() { print "c called"; true }
in
a() and b() or c();
prints:
a called
c called
How this works is, the macro is parsed and converted to a function
and a note made in the compile-time environment that it is a
macro. Then if it is invoked later, each of it's arguments
is wrapped in a thunk before passing them to the function.
Likewise within the macro body, any reference to a formal argument
a is replaced with a call to that argument a(). To return to the AND
example from above
macro AND(a, b) { if (a) { b } else { false } }
is rewritten to
fn AND(a, b) { if (a()) { b() } else { false } }
and likewise any call to AND(a(), b()) becomes AND(fn () {a()}, fn () {b()}) which a subsequent
generic optimization step will transform to AND(a, b), because fn () { a () } can be
reduced to just a. In summary
AND(a(), b()) => if (a()) { b() } else { false }
Which is just what is required.
As part of the macro work, we've finally added a true thunk type to the type system, so a function
of no arguments like fn () { 10 } no longer has the type number but instead has the
type #() -> number (thunk of number). Macros like AND above gain correct types, so AND is
(#() -> bool) -> (#() -> bool) -> bool.
While macros automatically have their arguments thunked and then forced (applied) at the point of use,
other functions can benefit from having lazy arguments. For that reason there are a couple of new
operators defined in the preamble: prefix & wraps its argument in a thunk, and prefix * forces a
thunk. There is no real value to these operators other than being slightly easier to type than the equivalent
fn () { x } and x().

CEKF(s) a.k.a. F Natural a.k.a F♮