1. You should make it clear in the README that this uses lazy evaluation, because most Lisps use strict evaluation.
2. `lambda` should have an implicit `begin` around its body to allow multiple forms, e.g.
(lambda ()
(define x 1)
(+ x x))
(as of commit 4e0eec383363, calling this lambda fails with "list too long").
3. The behaviour of the top-level forms changes when they are wrapped in `begin`. For example, the program
(car 1)
2
succeeds (due to lazy evaluation), but wrapping it in `(begin ...)` causes it to fail with "expecting a cons". If the intent is to use lazy evaluation, then both versions should succeed.
4. Since this dialect is pure (no side effects), it should be a syntax error if a non-final form in a `begin` expression expands to some expression which is not a declaration. For example,
(defun f (x)
(g x)
(h x))
should be a syntax error, because the call to `g` is useless (unless it's a macro that expands to `define`).
In the same vein, you could make it a syntax error if the final form in a `begin` expression is `define` (and likewise for the forms in an `if`).
5. The way `if` handles values other than true/false is backwards compared to most Lisps (and based on the comments in the code I think it's an accident). For example
(if "foo" "first" "second")
evaluates to "second", but in most Lisps it would evaluate to "first" (because strings are considered "truthy").
Hey, author of the library here, thanks for your comment! Disclaimer: It's been more than a year since I've written this, so I might've forgotten things.
This actually teaches me that one thing I should make it clear is this was essentially a weekend experiment from someone who has never used, let alone written a Lisp before.
There's no way it's a production grade software, there are places where the evaluator just borks for valid expressions.
I learned a lot when writing it, so I can probably do a large-ish refactor that would simplify many things and fix the inconsistencies.
> this uses lazy evaluation, because most Lisps use strict evaluation.
The evaluation semantics are pretty weird. It's lazy-ish mostly, but when evaluating there are a lot of places where I unnecessarily force evaluation of things. So I should make the semantics clearer and document them.
> The behaviour of the top-level forms changes when they are wrapped in `begin`
This is a bug, you are right .
> Since this dialect is pure (no side effects)
It's mostly pure. There are a couple of constructs like `assert` and `log` that perform side effects. And also as you mentioned the expressions in a begin can be macros that end up modifying the environment.
> The way `if` handles values other than true/false is backwards compared to most Lisps
You are likely correct and this is a bug!
---
Thanks again of your comments, I'll try to find some time to either rewrite the project, or at least document it better.
Hi! It looks quite polished for a weekend-style project :) But the "Current state" section is upfront about it. I should have noticed the commit date; I guess "archiving" the project would have sent a stronger signal.
Please don't rewrite it on my account (I'd just mention laziness and fix the `if` bug since it's trivial: https://github.com/utdemir/nixlisp/blob/4e0eec383363df72b7b4..., the comment already says the right thing). It would be interesting to see the "bork" examples if you still have them on hand (I'd even put them in the README).
It was fun to play with, thanks for making and publishing!
If it's pure, there's no benefit to a list of forms in lambda. As only the last one would do anything. If define is a mutator, the language isn't pure. So 2 and 4 are in conflict.
Perhaps it is pure excepting the top level, where mutations such as define are permitted.
Eh, that's just quibbling over the syntax. The `define` forms could be rewritten as `let` to make things look more "pure".
(The only situation where `define` might be considered "impure" is if it was used to redefine a variable that already existed in the same scope; and even then, it could still be considered "pure" as long as it created a new variable instead of mutating the original variable.)
1. You should make it clear in the README that this uses lazy evaluation, because most Lisps use strict evaluation.
2. `lambda` should have an implicit `begin` around its body to allow multiple forms, e.g.
(as of commit 4e0eec383363, calling this lambda fails with "list too long").3. The behaviour of the top-level forms changes when they are wrapped in `begin`. For example, the program
succeeds (due to lazy evaluation), but wrapping it in `(begin ...)` causes it to fail with "expecting a cons". If the intent is to use lazy evaluation, then both versions should succeed.4. Since this dialect is pure (no side effects), it should be a syntax error if a non-final form in a `begin` expression expands to some expression which is not a declaration. For example,
should be a syntax error, because the call to `g` is useless (unless it's a macro that expands to `define`).In the same vein, you could make it a syntax error if the final form in a `begin` expression is `define` (and likewise for the forms in an `if`).
5. The way `if` handles values other than true/false is backwards compared to most Lisps (and based on the comments in the code I think it's an accident). For example
evaluates to "second", but in most Lisps it would evaluate to "first" (because strings are considered "truthy").