summaryrefslogtreecommitdiff
path: root/html/notes/macros.md
diff options
context:
space:
mode:
Diffstat (limited to 'html/notes/macros.md')
-rw-r--r--html/notes/macros.md151
1 files changed, 151 insertions, 0 deletions
diff --git a/html/notes/macros.md b/html/notes/macros.md
new file mode 100644
index 0000000..3169c49
--- /dev/null
+++ b/html/notes/macros.md
@@ -0,0 +1,151 @@
+# Does the decoder implement macros?
+
+I've written about the [parser/decoder dualism](reader.html) in a
+previous article. Long story short, the parser takes care of syntax
+sugar, like turning `#(...)` into `(#HASH ...)`, and the decoder takes
+care of turning that into a vector or whatever.
+
+Now, since the job of the decoder seems superficially quite similar to
+that of a macro expander, I've been agonizing for the past two days or
+so whether it *is* the macro expander.
+
+(Warning: This post is probably going to be very rambly, as I'm trying
+to gather my thoughts by writing it.)
+
+On one hand, sure:
+
+ (define-syntax #HASH
+ (syntax-rules ()
+ (#HASH <element> ...)
+ (vector '<element> ...)))
+
+Or something like that. You know what I mean? I mean, in Scheme you
+can't return a vector from a macro, but in Zisp the idea is that you
+can very well do that if you want, because why not.
+
+It's very much possible that I will eventually realize that this is a
+bad idea in some way, but we'll see. So far I really like the idea of
+a macro just returning objects, like a procedure, rather than having
+to return a syntax object that has a binding to that procedure.
+
+This may be similar to John Shutt's "vau calculus" from his language
+Kernel. Maybe Zisp will even end up being an implementation of the
+vau calculus. But I don't know; I've never fully grokked the vau
+calculus, so if I end up implementing it, it will be by accident.
+
+In any case, I want the user to be able to bind transformers to runes,
+and doing so feels like it's pretty much the same thing as defining a
+macro, so maybe the decoder should also be the macro expander.
+
+But then there's an issue with quoting. Consider the following:
+
+ (define stuff '(foo #(0 1 2)))
+
+In Zisp, this would first of all be parsed into:
+
+ (define stuff (#QUOTE foo (#HASH 0 1 2)))
+
+Now, if #QUOTE didn't decode its operand, we'd end up seeing #HASH in
+the result, never creating the vector we meant to create.
+
+But if #QUOTE calls decode on its operand, and the decoder is also the
+macro expander, whoops:
+
+ (let-syntax ((foo (syntax-rules () ((_ x) (bar x)))))
+ '(foo #(0 1 2)))
+
+ ;; => (bar #(0 1 2))
+
+I mean... MAYBE that should happen, actually?! Probably not, though.
+What Scheme does isn't gospel; Zisp isn't Scheme and it will do some
+things differently, but we *probably* don't want anything inside a
+quoted expression to be macro expanded. Probably.
+
+The thought that I might actually want that to happen sent me down a
+whole rabbit whole, and made me question "runes" altogether. If they
+just make the decoder invoke a predefined macro, well, why not ditch
+runes and have the parser emit macro calls?
+
+So instead of:
+
+ #(x y z) -> (#HASH x y z)
+
+(Which is then "decoded" into a vector...) Why not just:
+
+ #(x y z) -> (VECTOR x y z)
+
+And then `VECTOR` is, I don't know, a macro in the standard library I
+guess. If the decoder is the macro expander, then sure, it will know
+about the standard library; it will have a full-blown environment that
+it uses to macro expand, to look up macro names.
+
+But no, I think this conflates everything too much. Even just on the
+level of comprehensibility of code containing literals, I think it's
+good for there to be something that you just know will turn into an
+object of some type, no matter what; that's what a literal is.
+
+(In Zisp, it's not the reader that immediately turns the literal into
+an object of the correct type, but the decoder still runs before the
+evaluator so it's almost the same.)
+
+Then again, maybe this intuition just comes from having worked with
+Scheme for such a long time, and maybe it's not good. Perhaps it's
+more elegant if everything is a macro. Don't pile feature on top of
+feature, remember?
+
+Booleans, by the way, would just be identifier syntax then. Just
+`true` and `false` without the hash sign. In Zisp, you can't shadow
+identifiers anyway, so now they're like keywords in other languages,
+also a bit like `t` and `nil` in CL and Elisp.
+
+IF we are fine with the quote issue described above, then I *think*
+everything being a macro would be the right thing to do. Although
+I've said the decoder could be used for things other than code, like
+for configuration files containing user-defined data types, you could
+still do that by defining macros and calling the macro expander on the
+config file.
+
+It's just that you would either not be able to have stuff like vectors
+in a quoted list (you'd just get a list like `(VECTOR ...)` in it if
+you tried), or you'd have to be expanding any macros encountered
+within the quoted list. Either both, or neither.
+
+Not getting a choice, you say... That's not very expressive. That
+seems like a limitation in the language. Remember: remove the
+limitations that make additional features seem necessary.
+
+Next thing we will have two variants of quote: One which quotes for
+real, and one that expands macros. Or maybe some mechanism to mark
+macros as being meant to be run inside a quote or not, but then we
+re-invented runes in a different way.
+
+Which brings me back to runes, and how `#QUOTE` could handle them,
+even if the decoder is the macro expander.
+
+Encountering `#QUOTE` could tell the decoder that while decoding the
+operand, it should only honor runes, not macros bound to identifiers.
+
+That would probably be a fine way to solve the quote problem, should
+the decoder also be the macro expander: Macros are bound to runes or
+identifiers, and the rune-bound macros are those that are expanded
+even inside a quote.
+
+I think that would be the same as having completely separate decode
+and macro-expand phases.
+
+(The reason we would want them merged, by the way, is that it would
+presumably prevent duplication of code, since what they do is so
+similar.)
+
+It's possible that I'm agonizing for no reason at all because maybe
+the decoder cannot be the macro expander anyway.
+
+We will see.
+
+For now, I think it's best to proceed by implementing the decoder, and
+once I've come to the macro expander I can see if it makes sense to
+merge the two or not.
+
+But I'll probably keep runes one way or another, since they're a nice
+way of marking things that should be processed "no matter what" such
+that they can function as object literals within code.