GithubHelp home page GithubHelp logo

Comments (25)

kanaka avatar kanaka commented on September 27, 2024 1

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.

kanaka avatar kanaka commented on September 27, 2024

@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.

boxed avatar boxed commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

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.

kanaka avatar kanaka commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

"modify exiting environments"

middle word should be "existing"

from mal.

kanaka avatar kanaka commented on September 27, 2024

Done.

from mal.

boxed avatar boxed commented on September 27, 2024

nice! here's another one :P

"symbol "let": create a new environment"

Should be "let*"?

from mal.

boxed avatar boxed commented on September 27, 2024

"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.

kanaka avatar kanaka commented on September 27, 2024

Fixed. Keep them coming!

from mal.

boxed avatar boxed commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

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.

kanaka avatar kanaka commented on September 27, 2024

@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.

boxed avatar boxed commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

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.

kanaka avatar kanaka commented on September 27, 2024

@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.

boxed avatar boxed commented on September 27, 2024

Step 7: nothing to add! W00t! :P

from mal.

boxed avatar boxed commented on September 27, 2024

step 8: should mention that tests will try functions nth, first, rest

from mal.

boxed avatar boxed commented on September 27, 2024

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.

keith-rollin avatar keith-rollin commented on September 27, 2024

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.

boxed avatar boxed commented on September 27, 2024

Ah. I guess I was thrown off by the asymmetry with how first should work. Thanks!

from mal.

kanaka avatar kanaka commented on September 27, 2024

@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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.