4 ms·
> 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
by vdupras 2y ago
> 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.
- kragen 2y ago"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!
- vdupras 2y agoYeah, 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
- kragen 2y agoThat 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.
- anthk 2y agoCan you write a ZMachine interpreter?
- vdupras 2y agoOf course I can, and so can you!
- anthk 2y agoI'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 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