orangeduck/BuildYourOwnLisp

question on the need for Q expression?

Closed this issue · 3 comments

Hi,
Really like the project btw, I've never used LISP and find this a great intro into it and good to get a hands on feel, rather than saying trying to Learn Clojure right away...
My question is, is there really need a for 'Q expression'. If you treat quote as a symbol.
Then you your 'eval' could change,
you change the order of the eval, so rather than:

eval : if symbol lookup, if s-expression - eval, otherwise return value ...
to
eval if symbol - , if quote - call eval on the next expression (Which eventually would be consulting some 'macro table', if def - ... , ... if s-expression - eval, otherwise return value ...

It seems like, even if you don't want to implement macro expansion, you could still effectively get rid of 'q expression'
and just test for a quote or def or lambda at the start of the eval and treat the next expressions appropriately?

You're correct that you can perfectly well use macros instead of Q-Expressions and essentially you can get the same thing - the quote macro is a bit like the Q-Expression indeed. There are subtle differences and pros and cons on both sides. Probably a real macro system is better for a kind of industrial strength Lisp, but I find Q-Expressions kind of nice in terms of simplicity and ease of learning.

To be a bit more precise about the comparison - a Macro is like a function that does not evaluate its arguments first: so if def is a macro then (def x blah) is kind of like (def {x blah}) using Q-Expressions. Some macros are also special macros and do special things e.g. assigning variable names, while in our lisp some functions are special functions and do things like assigning variable names.

Some arguments in favor of Q-Expressions over Macros:

  • With Macros there is no syntax distinguishing between a function and a macro. The only way to know if (blah x y) is going to evaluate x or not is to know if blah is a macro rather than a function. For this reason I think Q-Expressions can be easier spot and to intuitively understand.
  • Because you can write functions that return Q-Expressions you can build a Lisp using fewer builtin functions (i.e. just assignment and lambda), making things more homoiconic and simple.

Some arguments against Q-Expressions over Macros:

  • Q-Expressions make lexical scope difficult to implement compared to Macros, which can cause obscure and difficult variable shadowing bugs due to dynamic scope. (However, a hygienic Macro system is not that easy to implement either).
  • In some places you need to work around things being eagerly evaluated by wrapping them in Q-Expressions e.g. the head function.
  • Q-Expressions are fairly unusual/uncommon in other Lisps.

I actually think either is fine - if you want to switch the language to using Macros I think that is a pretty good exercise 👍

Thanks, I managed a first cut of a C++ version https://github.com/lewismj/inky , I'll update my docs, to better explain things re: Q-Expressions and Macros. Enjoyable experience. Picked this up as an exercise to learn Spirit - I was going to try to use Boost Spirit. I'm familiar with FastParse (Scala) and Parsec (Haskell) but ... gave up with Spirit.

Thanks very much for the explanations. Really appreciated!