Comments (25)
I think the improvements and review from this have all been incorporated. If there are more please open a separate issue (or even better, a PR with changes to review).
from mal.
@boxed thanks for the feedback. You're right that the guide is still somewhat rough and has incomplete spots. I've tried to mark the really glaringly incomplete parts with TODO/TBD.
I added a parenthetical comment for step0 to indicate that the prompt needs to be "user> ". ("mal-user> " is what the mal (self-host) implementation uses which is why the tests check for that too). I also fixed the typo.
Yep, those other types are currently marked "TODO/TBD". At some point when I get some breathing room from my PhD classes/research I'll try and fill the gaps.
To give some context: my intention with the guide is not to provide answers for every small detail, but rather to provide enough structure and guidance that even casual developers will have enough guidance to solve the challenges they might encounter. It's really intended to be useful as a learning exercises: both for learning new languages and for learning about Lisp in general. I should probably also emphasize somewhere in the guide that the process is intended to be test driven. In some sense the collection of tests is a loose specification for how the language should behave at that step. In other words, part of the point of the tests is to explain how the language should function and not just answer the question of whether it works or not.
And feel free to fork, modify the guide and send me a pull request. No guarantee that I'll merge it (of course), but if it maintains the style and improves the guide (e.g. enhances the learning aspect) then I'll probably merge it in. But I appreciate your feedback and keep it coming as you work through the guide.
Out of curiosity, what language are you working on and how would you self-rate your development skills.
from mal.
I'm doing python3 as a fun exercise.
My level is pretty high. ~14 years of professional programming. 9 C++, 4 Python. And several years hobby coding before that. I'm also self taught.
from mal.
There's also some mismatch between the tests and the guide. For example the guide says that number and lists are required in step 1, but the tests requires quoting, quasiquoting, unquoting and splice-unquote.
from mal.
The general hints section notes that optional guide sections and optional tests are not quite the same. But you're the second one to mention this so I've changed the word the guide uses to "deferrable".
from mal.
"modify exiting environments"
middle word should be "existing"
from mal.
Done.
from mal.
nice! here's another one :P
"symbol "let": create a new environment"
Should be "let*"?
from mal.
"However, our little interpreter has not quite reach Lisp-ness yet. The next several steps will take"
Should be "reached". And that sentence just ends abruptly
from mal.
Fixed. Keep them coming!
from mal.
Extra "the" in "call to evaluate all the parameters the except for"
"do: change the eval_ast call to evaluate all the parameters". Weird, my code from last step doesn't have a call to eval_ast, but a call to EVAL. All tests run and reading the instructions again it seems I've followed them. Looking at the python implementation in mal it does indeed do eval_ast but if I change to do that I don't pass the tests. Something is fishy, but I don't know what :P
"fn: the original function value return in step 4 (this is actually deferrable until step 9 when it is needed for the map and apply core functions)." <- I've read that a bunch of times but I can't understand what it's supposed to mean. Does "the original function value return in step 4" mean just "the same closure as in step 4"? Hmm.. reading the existing python implementation it seems to just mean "EVAL".
"regular function (not one defined by fn*): apply/invoke it as
before (in step 4)." <- two bullet points, but it should just be one
from mal.
The recommendation to put things in reader.qu, core, printer, etc is something I'm finding a bit doubtful. It makes the steps in the guide interfere with previous steps, which is a bit bleh in my opinion. Better to KISS and just have one file.
from mal.
Step 5 has other issues, like tests failing that SHOULD fail but they are still written out as failed. This BEFORE tests that are in fact mandatory. It's super confusing!
from mal.
The timeouts on tests is super annoying too. Why does the tests wait for "foo\nuser>" for 10 seconds when it has already gotten a huge stack trace? This seems to me to be just annoying and slow down the guide and make you want to stop doing it.
from mal.
@boxed Sorry you're getting bogged down :-( But thanks for providing feedback to improve things so that others are less like to run into the same things. I wish it could be perfect from the beginning, but this is just a side project I do in my free time. Also, even if I had 6 extra hours every day, there would probably still be gaps and inconsistencies because I've been working on mal for a couple of years now and I can no longer put myself into the shoes of somebody coming to mal for the first time.
I just pushed some changes to incorporate your suggestions from 3 days ago and I've also re-ordered step5 tests to put the ones that should pass first.
Regarding EVAL vs eval_ast in the do
form. EVAL shouldn't work because EVAL of a list will treat the first argument as a function to call/apply. Unless you mean you iterate across each element of the do
form calling EVAL separately on each of them, in which case, that's exactly what eval_ast should do with a list.
The split between code that goes in steps and code that goes into other files isn't completely arbitrary (a bit but not completely). My rule of thumb is something like this: If it's code specific to implementing a Lisp then it goes in the step files. If it's code for implementing new dynamic data-types/objects and the functions/methods that operate on those types, then it goes in separate files. If the target language has types and functions that resemble mal types, then those files tend to be very small or non-existent. Examples:
- the mal implementation has no types, reader, printer files and trivial core
- the clojure implementation has no types file and fairly trivial reader, printer files (just to modify the clojure reader/writer slightly) and a fairly trivial core
- interestingly, ruby types and the functions that operate on them are very "Lispy" so Ruby's types and core files are very small.
The env module is somewhat more arbitrary, however, it's a self-contained module that is implemented early and changes very little after that, so I decided to separate it. Also, for languages that have hierarchical maps/dictionaries (e.g. Javascript objects/prototype chain), you don't need an env module.
Or another way of summarizing is that the step files represent the core of what makes something a Lisp, the rest of the modules are just language specific details (they may be the harder than the Lisp part, but that's due to the nature of the target language not because of Lisp per se).
BTW, there is an issue (#40) to improve runtest.py to detect when the implementation has crashed and immediately exit with a fatal error. It won't fix the problem where timeouts cause runtest.py to get out of sync for the remaining tests. That needs to be fixed too. If only there were 6 more hours in every day.
from mal.
I wouldn't say I get bogged down exactly.. I am progressing quite nicely given that I have very little time to fiddle with this :P
Really I think the guide is quite good, but it can obviously be better! I absolutely understand that it's impossible to see your own project with new eyes. That's why I'm taking so much of my time to document these confusions I'm having even though I can obviously figure my way out of them. It's very easy to just not bother giving this type of feedback!
In a way it's a bit disheartening to see you haven't got more constructive feedback like this given how much attention this project has gotten :(
from mal.
More feedback!
"In your main program, add a new eval (symbol) entry to your REPL environment...."
This whole section is very specific and complex. I think it'd be better to describe what it should do in simpler terms... like "In your main program, add the symbol "eval" to your REPL environment. It's just a function that takes an AST and runs it. So "(eval (+1 1))" = 2, just like "(+ 1 1)". The eval function should have the global env as env."
Or something.. my suggestion above isn't great, but I think it's simpler and easier to grasp :P
from mal.
Section 6: when you run the tests you get failures on "Testing that *ARGV* exists and is an empty list". There is no mention of this *ARGV* in the guide at all! I'm guessing I should set it to an empty list for now? :P
Aha, I see now you have the same problem I had when trying to write this comment. You need to have *ARGV* in your markdown so you don't end up with italic, but keep the *
from mal.
@boxed I reworded the text regarding eval to try and simplify the text and also expanded the conclusion text of step6 to describe why eval is so interesting. Let me know if that clarifies it any for you.
And yes, the ARGV issue was due to markdown making it italics rather than preserving the stars. I've fixed that now too.
from mal.
Step 7: nothing to add! W00t! :P
from mal.
step 8: should mention that tests will try functions nth, first, rest
from mal.
This makes me confused:
TEST: (def! x "x") -> ['','*'] -> SUCCESS
TEST: (def! x (nth '(1 2) 2)) -> ['','*'] -> SUCCESS
TEST: x -> ['','"x"'] -> FAIL (line 10):
Expected : 'x\r\n"x"'
Got : 'x\r\nnil'
Why should (def! x nil) be ignored? Seems weird to me.
from mal.
The guide says:
nth
: this function takes a list (or vector) and a number (index)
as arguments, returns the element of the list at the given index.
If the index is out of range, this function raises an exception.
I take that to mean that the second expression doesn't complete, and so x remains unchanged, meaning that it stays set to "x".
from mal.
Ah. I guess I was thrown off by the asymmetry with how first should work. Thanks!
from mal.
@boxed I re-ordered the tests in step8 to better match the guide by pushing nth, first, rest down a bit. That way the first tests are directly about basic macro functionality.
And yes, for core functions I've largely kept to the behavior of Clojure core functions. It is a bit inconsistent that nth throws with invalid index but first, and rest return nil but that's the way it is. And as @keith-rollin notes, whenever a form throws an exception, any enclosing expressions will be skipped out to the first try/catch form (which you'll implement next in step9). This means the enclosing (def!) form never happens so x remains unchanged. One thing to be aware of though is that any side-effects earlier in a do statement will still happen. For example:
(do (prn "foo") (throw "cancel") (prn "bar"))
In that case "foo" will be printed, but "bar" will not be because the elements of the do form are evaluated one at a time in order. Just something to be aware of.
from mal.
Related Issues (20)
- what is the minimum requirement for the base language? HOT 2
- Verilog: is there a good reason mal has not been implemented? HOT 1
- VHDL: defmacro! doesn't modify existing functions HOT 1
- make "test^js^step0" fails -- Fatal error in , line 0 -- Check failed: result.second. HOT 1
- PDFs?
- Somebody should solve Rosetta Code challenges with mal
- Mal in Coq? HOT 2
- Apparently in step 3 'env' is a class or structure type, not an object HOT 2
- Questions on different design from lisp: Mal symbol do not use property lists? HOT 1
- Lots of unanswered merge requests HOT 1
- Why is quasiquote so complicated? HOT 3
- What with `` gensym '' HOT 2
- lexicial versus dynamic HOT 1
- Floats don't work in the python implementation. HOT 2
- Is #mal IRC channel still active? people seems tends to use discord now XD HOT 1
- Should `eval` use the top-level environment? HOT 1
- How is `cond` supposed to work? HOT 2
- Remaining impls to combine eval-ast/macroexpand into eval HOT 10
- common-lisp fails to build HOT 1
- Fix self-hosted test failures HOT 30
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mal.