4 ms·
Reading the related article on writing a Lisp interpreter in Haskell (http://news.ycombinator.com/item?id=4764088 http://news.ycombinator.com/item?id=4764088) r
by gliese1337 14y ago
Reading the related article on writing a Lisp interpreter in Haskell (http://news.ycombinator.com/item?id=4764088 http://news.ycombinator.com/item?id=4764088) reminded me of my second blinding moment of enlightenment- understanding vau expressions. Things that can't be implemented as functions are typically things that require controlling the evaluation of arguments (conditionals, assignment, short-circuiting boolean operators, etc.), and additional language features (built-in special forms or macros for writing your own) are included to handle those. But if you have something that allows you to control the evaluation of arguments, simply choosing to evaluate all your arguments gives the equivalent of a function. Implement that thing, and your compiler/interpreter no longer needs to know about the difference between functions and macros and built-in forms; they're all the same thing!
There's not a lot of practical use for that kind of thing that I am aware of (implementing run-time macros is one, being able to pass short-circuiting boolean operators to map, reduce, etc. is another), but I strongly suspect that's just because we don't have 30 years of collective experience figuring out all of the great things about vau expressions like we have with Lisp and anonymous functions. The only language (discounting toy projects) I know of that actually implements them is Kernel (http://web.cs.wpi.edu/~jshutt/kernel.html http://web.cs.wpi.edu/~jshutt/kernel.html).
- Peaker 14y agoA language that is lazy by default lets you control the evaluation of arguments. Ordinarily, they're not evaluated, and if you force them they are. However, macros are not just about whether to evaluate -- but about exposing the internal syntactic structure of the arguments. In Haskell, using laziness you can implement control flow, short-circuiting, etc. If you want functions that work with the syntactic structure of their arguments, you need heavier machinery: * Thick DSLs: Define explicit AST types and have a DSL that explicitly constructs those AST's. * Template Haskell (the arguments' syntax has to be in ordinary Haskell) * Quasiquotes (Need to parse strings) I think the need for exposed syntax is relatively rare (e.g: a function that evaluates and shows a trace of the evaluation). In those cases, I think explicit AST types work pretty well, as Haskell has extremely light-weight syntax for constructing user-defined data types.
- gliese1337 14y agoWithout access vocal inflection, I'm not sure if you're intending to argue, or expand. So, I'm gonna go with continuing to expand on the point. Simple laziness does not allow you the same level of control over evaluation as vau expressions do. A vau expression can choose to evaluate it its arguments exactly once (like call-by-value), exactly as many times as they're used (like call-by-name), only if they are used (like laziness), as many times as you feel like, in a different environment than the calling context, or not at all, and can make that decision independently for every argument. In Kernel's implementation at least, unevaluated operands are AST types that can be poked and modified, not opaque values like lazily-evaluated operands. As a result, vau expressions can be used to implement macros, both the hygenic and non-hygenic variety, and the language need not define quoting or quasiquoting because those features can also be implemented within vau expressions. Vau expressions seem to play havoc with static analysis, though, so there are good arguments for actually having some of those things as built-in language features rather than just building everything as a standard library.
- Peaker 14y agoI was expanding (with a slight correction about macros doing more than just controlling evaluation). Haskell-style laziness comes with purity, where it does not matter much whether something is evaluated once or many times. It does matter if it is evaluated 0 or more though (due to non-termination and exceptions). The opacity of values is what I meant by macros also exposing the syntax as opposed to just controlling evaluation.