Open mattbierner opened 9 years ago
Nevermind. That's a stupid approach.
Instead add two operators (placeholder ops used here until finalized):
\\
- Create thunk
#
- Eval Thunk
where:
\\
is a macroish unary operator that takes an expression and puts it into a thunk.
and #
is a regular unary operator that does:
var (#) := \x -> ?isThunk x : x() : x;
Together these can be used to write call be need expression that inlining can then optimize to eliminate the overhead in normal cases.
var maybe := \ val con alt ->
?isNothing val : #alt : #con;
maybe(v, \\new Leaf(h, k, v), empty);
expands to
var maybe := \ val con alt ->
?isNothing val : #alt : #con;
maybe(v, \-> new Leaf(h, k, v), empty);
Inlines to:
var maybe := \ val con alt ->
?isNothing val : #alt : #con;
let val = v, con = \-> new Leaf(h, k, v), alt = empty in ?
?isNothing val : #alt : #con;
Inline to:
let val = v, alt = \-> new Leaf(h, k, v), con = empty in ?
?isNothing val : (?isThunk con : con() :con) : (?isThunk alt : alt() : alt);
const prop to:
let alt = \-> new Leaf(h, k, v) in ?
?isNothing v :(?isThunk empty : empty() :empty) : alt();
Inline to:
?isNothing v :(?isThunk empty : empty() :empty) : new Leaf(h, k, v);
Inlining framework already has most of the code in place to support a simple macro type system that uses call be need evaluation. This is required in order to allow programers to write code that can express the same logic as the builtin
&&
and||
ops.Potential syntax (using hamt code example):
We can't create a helper function
maybe
for this because even ifv
is nothing, thenew Leaf(h, k, v)
arg is evaluated.Using call by need instead:
Expands to:
Using
static
for call by need, each argument must be a valid expression and the body must be a valid expression on its own. But instead of binding values to arguments, the body is rewritten at compile time to replace instances of identifies with the appropriate sub expression.Static may be convertible to regular functions, at which point it would revert to using call by value instead.