
Building a Lisp from scratch with Swift - ingve
https://www.uraimo.com/2017/02/05/building-a-lisp-from-scratch-with-swift/
======
falcolas
Once again pimping one of my favorite DIY projects - Make A Lisp. Lisp in
(almost) every language!

[https://github.com/kanaka/mal](https://github.com/kanaka/mal)

I find it to be a great tool for both learning about Lisp and the language
you're writing your mal implementation in.

------
ue_
I have never used Swift, but I can confirm that writing a Lisp is a lot of fun
in something like C or Go. I especially like the fact that you can build in so
much raw _power_ by implementing Lisp macros for example.

~~~
nickynickell
Take it a step lower and do one in assembly and it is even more fun. I almost
have enough code together to say that I'm doing one in RISC-V assembly. At
this point I have already accidentally picked up a more intuitive
understanding of how a lisp compiler works.

~~~
tomcam
Assembly? Hell, I did mine in microcode, VHDL, and telepathy using a tachyon-
driven IDE. It was done before I started.

~~~
mirekrusin
What about this guy who created universe where life evolved and wrote lisp
interpreter for him. That is low level my friend.

~~~
tomcam
I am constantly being outclassed

------
archevel
I wrote a lisp in Go a while back and got to the point where it could parse
and interpret basic lisp code. I got stuck on making it do proper tail call
optimization since I didn't want it to do trampolining. Since Go doesn't
support TCO the only way I came up with was to rewrite the interpreter either
use a big while loop or gotos instead of relying on function calls. Does Swift
do TCO making this trivial? Anyone else solved implementing a lisp with TCO in
an elegant way in a non-TCO language?

~~~
zephyrfalcon
One way is to model the call stack explicitly. I.e. you will need objects (or
structs, in Go) representing a stack frame, and have a stack of those (the
actual call stack). Evaluation works very different that way, since you can no
longer piggyback on the implementation language's call stack. It does mean
that you can now do the desired tail call optimizations, e.g. if you're
evaluating (if cond a b), and you've evaluated 'cond', then you can replace
the if's stack frame with one containing just a or b, and continue evaluation
there.

One benefit of this is that continuations become trivial to implement (they're
just a snapshot of the call stack at a given point).

Admittedly, almost all write-your-own-Lisp tutorials omit this, and use a
bunch of recursive calls (in the implementation language) instead, which makes
it very hard to do TCO, continuations or anything else that requires a real
call stack (exception tracebacks, for example).

(I hope this answer makes sense; it's 3 in the morning here, so clarity might
suffer a bit. ;-) But as it happens, I am writing a (hopefully non-toy) Lisp
interpreter myself, in Go, so the question attracted my interest.

~~~
taejo
> anything else that requires a real call stack (exception tracebacks, for
> example).

You can go full metacircular and have the interpreter throw an exception and
introspect its traceback, in languages that support that.

------
melling
A couple of other Lisp in Swift examples:

[http://www.h4labs.com/dev/ios/swift.html?age=10000&q=lisp](http://www.h4labs.com/dev/ios/swift.html?age=10000&q=lisp)

------
kruhft
Can you type declare the fact that NIL is both a list and an atom in Swift?

~~~
akkartik
It doesn't have to be. In Common Lisp _nil_ is a symbol but not a list. In
Racket _' ()_ is a list but not a symbol (or a cons).

FWIW, in my rite-of-passage lisp-in-C implementation the type of _nil_ and
_()_ is _nil_ , just like the _car_ or _cdr_ of _nil_ :
[http://akkartik.name/post/wart](http://akkartik.name/post/wart)

~~~
reikonomusha
In Common Lisp, NIL is a symbol and a list and a Boolean.

~~~
akkartik
Ah, you're right. It's a list but not a cons.

    
    
      $ sbcl
      * (symbolp nil)
      T
      * (listp nil)
      T
      * (consp nil)
      NIL
    

Is there a boolean predicate? My Common Lisp is rusty and I don't see one at
[https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node73.html#S...](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node73.html#SECTION001022000000000000000).

~~~
kazinator
Also, the type of NIL in Common Lisp is NULL. There is a NIL type, but it is
empty: there exists no value which is an instance of the NIL type. NIL is a
subtype of every type (including itself), and the type T is a supertype of
every type, including itself. This situation with NIL and T on opposite ends
of the "type spindle" is quite clever and subtly better than making NIL a type
which contains NIL.

~~~
akkartik
Thanks for the pointer! I'd never seen
[http://www.lispworks.com/documentation/lw70/CLHS/Body/t_null...](http://www.lispworks.com/documentation/lw70/CLHS/Body/t_null.htm#null)
before, in spite of poring over the CLHS for many hours over almost a decade.

Can you suggest any example Common Lisp programs that print the _types_ NULL
or NIL or T? Or show off the benefits of this spindle arrangement? I can
intuitively see the benefits of this approach, but an example makes things
much more memorable.

~~~
kazinator
The nice thing about T is that it's the first letter of True and of Type. E.g.

    
    
      ;; catch-all method for any type
      (defmethod foo ((arg t)) ...)
    
      ;;
      (defmethod foo (arg ...) ...) ;; alternative spelling: omit class
    

NIL is the empty list and that is mnemonic for "empty type". For instance, the
intersection of two mutually exclusive types is the empty type. Nothing can be
both a cons and an atom, so the type denoted by the type expression (and cons
atom) is empty; and that is nil. The set manipulation which underlies types
needs an empty set, and nil fill the need for that empty set (pun intended).

NULL is useful in writing methods that capture NIL (the only instance of
NULL). That may be recognized as the "null object pattern":

    
    
      (defmethod foo ((arg null))
        ;; (foo nil) called
        )
    
      (defmethod foo ((arg number))
        ;; (foo <number>) called
        )
    
      (defmethod foo ((arg t))
        ;; (foo <anything else>) called
        )

------
Yan_Coutinho
I watched this guy developing a productivity app in Swift
([https://www.liveedu.tv/zzzel/videos/8OX1z-building-
productiv...](https://www.liveedu.tv/zzzel/videos/8OX1z-building-productivity-
app-swift-2)) and it made me want to go back to Ionic.

