Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is too much to bear and I had to create an account for this!

Might I interest you in the opposite? Sort of a Lisp implemented in Forth that can compile Lisp functions to native code, all of this in less than 500 lines of Dusk code.

https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/doc/co... http://duskos.org/



Condolences on finally opening an HN account. I greatly appreciate your work even though I disagree with much of it. DuskOS in particular seems excellent.

I hadn't looked at comp/lisp before. It definitely looks like a real Lisp to me. In https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/mem/co... I see that you have dynamic typing, implemented in assembly, and from https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/mem/co... to line 70 you have a garbage collector. From https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/comp/l... I see you have closures, but I'm not sure how memory allocation for their captured variables works; from https://git.sr.ht/~vdupras/duskos/tree/master/item/fs/comp/l... I infer that you are just using dynamic scoping, thus ruling out both upward funargs and tail-call elimination?

What's the most interesting program you've written in comp/lisp?


> What's the most interesting program you've written in comp/lisp?

None. I wrote comp/lisp because my lack of Lisp knowledge seemed to me like a liability as I invest more and more time in Forth. I had nothing I felt like writing in Lisp, so I thought it would be fun to implement one.

While I can't call myself a Lisp programmer, I think I can say now I have an idea of what it's about. It didn't grow on me, so I leave it aside for now. Maybe I'll come back to it later.

> I infer that you are just using dynamic scoping, thus ruling out both upward funargs and tail-call elimination?

Tail-call elimination is possible with my implementation within the same function, but it's not "natural". I'm not sure of what it would take to have it like we see them in other lisps, or if it's possible at all. It's been a while since I swam in that pond, details aren't fresh in my mind.

As for scoping, I'm unfamiliar with the theory. I don't think it's what we call "lexical scoping" because variable names have very little meaning in the compilation process. It's eliminated very early and only "compile time Parameter Stack offset" is kept in the AST.

I don't know what you mean by "upward funargs", but if it's to access arguments from outer scope, yes it's possible. That's exactly the point of the last link you pointed to: lambda generation. A call to a closure generates a lambda which has its outer scope "injected" to PS.


"Upward funargs" is where you return a closure which captures the bindings of your local variables, so it can access arguments from an outer scope which no longer exists. Like this:

    scheme@(guile-user)> (define (make-adder x) (lambda (y) (+ x y)))                                                 
    scheme@(guile-user)> (define x+3 (make-adder 3))
    scheme@(guile-user)> (define x+4 (make-adder 4))
    scheme@(guile-user)> (x+3 7)
    $1 = 10
    scheme@(guile-user)> (x+4 8)
    $2 = 12
In Scheme those bindings are even mutable:

    scheme@(guile-user)> (define (counter n) (lambda () (set! n (1+ n)) n))
    scheme@(guile-user)> (define booga (counter 53))
    scheme@(guile-user)> (booga)
    $3 = 54
    scheme@(guile-user)> (booga)
    $4 = 55
This makes Scheme closures a fully general substitute for Smalltalk-like objects.

Upward funargs generally require a non-stack-like allocation discipline for the local-variable bindings.

While implicit variable capture like Scheme is the most common way to do closures, it isn't the only way; C++ and old versions of Python (before nested_scopes) have a form of closures where you instead explicitly list the variables you want to capture. Old dynamically-scoped Lisps like Lisp 1.5 had a different solution which I don't understand; see AI memo AIM-199, "The function of FUNCTION in LISP".

Usually dynamic scoping is incompatible with tail-call elimination (except in the special case of tail recursion) because you can't eliminate a tail call if which you have more work to do after it returns; usually popping some variable bindings off a context stack amounts to "more work to do". Maybe your parameter stack has a way for that to happen implicitly, for example by implicitly restoring a parameter-stack offset on function return.

I hope this helps. Keep being awesome!


Yeah, to be honest I'm too out of this context to speak of deep technicalities, but a quick test seems to indicate that Dusk's comp/lisp can do something similar to your first example, unless I misunderstood it:

  $ make run
  stty -icanon -echo min 0; ./dusk ; stty icanon echo
  Dusk OS
  79KB used 31MB free ok
  needs comp/lisp
   ok
  lisp (defun make-adder (x) (lambda (y) (+ x y)))
   ok
  lisp (defun x+3 (x) ((make-adder 3) x))
   ok
  lisp (defun x+7 (x) ((make-adder 7) x))
   ok
  lisp. (x+3 7)
  10 ok
  lisp. (x+7 8)
  15 ok


That could work without upward funargs because, in your version, (make-adder 3) and (make-adder 7) aren't live at the same time, so they might be using the same "x" instead of two separate "x" bindings. My hunch is that that "x" is in memory space that would be overwritten if you called another largish function in between calling make-adder and invoking its return value, but I haven't grokked comp/lisp fully enough to be sure that that's true.


Can you write a ZMachine interpreter?


Of course I can, and so can you!


I'm still trying to fix Malyon from Emacs, as it hangs on this free adventure (Spiritwrak)

https://jxself.org/git/?p=spiritwrak.git;a=summary

You can get inform6 from the same page of the repos, and informlib by cloning spiritwrak with

        git clone --recursive https://jxself.org/git/?p=spiritwrak.git;a=summary


Thanks for the excellent work you're doing! It's (extremely) high on my list of things-to-do if I ever get more time for learning and progressing my very rudimentary programming knowledge.


This is excellent as well. I don't really jive with the Collapsnik aspect but I absolutely love powerful systems that can run in constrained environments.

Thanks for sharing, I'll be diving into this for a while


I came in to this thread to ask if anyone had done this, given an intuition that it seems like a fairly natural path to bootstrapping lisp on metal. Didn't even have to ask!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: