27 comments

[ 3.9 ms ] story [ 115 ms ] thread
Nice write up. I also have the use many Lisp languages habit.

Not mentioned: Hy (hylang) that adds Clojure-like syntax to Python, and some other super fine Scheme implementation’s not mentioned in the article.

some other super fine Scheme implementation’s(sic) not mentioned in the article

Yeah, MIT Scheme has Edwin and a more-or-less CL-like debugging experience, Gauche has a hardware store's worth of batteries included, Loko compiles down to machine code without passing through C, Chez is fast, and Cyclone has a nice GC.

Last I checked, no Scheme produces code that runs as fast as CL (most performant scheme is Chez (recently open-sourced) )

Same with REPL functionality. Same with libraries available.

> That said, Com­mon Lisp is weird. What I find par­tic­u­larly jar­ring is that func­tions and vari­ables live in dif­fer­ent name­spaces:

Lisp-2s aren’t the only languages in which functions and variables are different namespaces - the same is true of PHP, Perl, Bash, etc. But the later mark the distinction through syntax - variables start with a dollar sign, functions don’t. I think there is something to be said for that approach, compared to the Lisp-2 way it is easier for beginners to understand, and it is instantly obvious whether a symbol is a variable or something else like a function, while also avoiding the Lisp-1 problem of built-in function names clashing with those of popular variable/argument names. I wonder why there are so few Lisp-like languages which adopt the “sigil solution”. (Common Lisp does have sigils, such as :keywords, but not for variables.)

A standard practice in Common Lisp distinguishes special variables by "earmuffs": the symbol names of special variables, by convention, start and end with asterisks. No requirement for this exists in the language definition, but just about everyone follows it.

It would be really annoying to have the same sort of de facto requirement for function names. Making the programmer use funcall or apply when calling a function value is much less intrusive, especially since CL idiomatically doesn't have the tail call fetish that Scheme seems to have.

That's to distinguish global variables though.
Common Lisp doesn't have "global variables", it has "special variables", which are subtly different.
Although Common Lisp is called Lisp-2, there are actually many more namespaces... I think I counted 5 or 6 the last time I tried. But it is pretty sensible to keep functions in a different namespace. For instance, it allows you to: (let ((let '`(let ((let ',let)) ,let))) `(let ((let ',let)) ,let))
That's sensible? I do not think that word means what you think it means...

I mean, yes, having functions in a different namespace may be sensible, or at least defensible. That example is ridiculous.

It's like `make make make make`. But it is nice that in CL you don't have to jump through hoops on common words that happen to be function or package names. You have a list and no meaningful name? Call it `list`:

  (defun some-algo (list ...) ...)
Sure you can abuse this, everything can be abused. But I'd rather risk an asshole doing that `let` thing (which wouldn't make it past code review) than have more name collisions and naming contortions to avoid them. If the language at least warned on renaming it would be helpful, but then you get things like Go. When I was learning it I accidentally overwrote some built-in function (I don't recall which one, it wasn't actually `make` like in this demo):

  package main
  func main () {
      make := 10
      aMap := make(map[int]string) // this is now an error
  }
I wish I could remember what it was, it was a reasonable name in context (unlike `make` above). But it had a similar effect and a similar strange error. The error is at the function call site, which is technically correct because you're trying to use an `int` (here) as a function. But `make` (above) and similar functions should probably get a warning emitted when you redefine them, at a minimum.
If Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo, might as well let let let let let.

In seriousness I like the ability to make such distinctions even if it can be a huge footgun.

In practice it is not a footgun at all! If you use Lisp, you can tell what's a function and what's a variable by position - functions are always first in a list, variables - never, (except in bindings)... etc.
There is this thing we call humor. Try running the example!
I wonder, how hard it would be to write something like xv6: https://github.com/mit-pdos/xv6-public

But in Lisp/Scheme? Preferrably no C. Just Lisp/Scheme and assembly.

Yes, please. I'd like a Lisp/Scheme/Racket programming environment on an indigenous A.I. chip minus the Von Neumann bottleneck to memory.
Have you seen Mezzano? It's a pure Common Lisp Operating system, albeit there are few C programs that were transpiled to CL to run on top as well (IIRC, DOOM :D)

It's somewhat limited but AFAIK it could in theory be made to self-host (right now IIRC image building is done separately from another system, but that's mostly a case of some programming work)

I feel most "at home" in Common Lisp, though I agree that Scheme is more elegant. Speaking of elegant I recently had some fun playing around with Janet (https://janet-lang.org) and I have to say it is quite nice.

If I had to pick a language to stay in forever and I had the time to really grow it into what I wanted using C extensions I'd probably choose Janet (or make my own similar lisp).

Like most people, though, I have a day job and until I decide to retire I won't have time to reinvent an entire personal programming system for myself. For pure "get in there and have fun" sessions after work Common Lisp gets my vote despite its clunkiness. And besides... it's a lisp! If something's too clunky I'll just fix it.

Janet-lang looks awesome and deserves more marketing!
Janet is very similar to s7 Scheme too, a minimal scheme for embedding that borrows heavily from CL. I use s7 for Scheme for Max, the author made it for various other computer music purposes.
It's nice to see the CL ecosystem evolving. SBCL sees regular updates with new optimizations. The editor support is getting better: Vim, Atom, Sublime, VSCode… have good to very good support[0], & Jupyter notebook, the Lem editor… and a new lisper started a CL editor based on Tauri: Parrot https://github.com/fonol/parrot. Cool projects emerge (lisp-stats , the Sento / cl-gserver actors library, the Kons-9 3D graphics library, the CLOG web-gui…)

> 50MB

With compression (zstd now), SBCL binaries weigh ±25MB. Start-up time is super fast. I built a standalone binary for my web app, it is straightforward to start it on the background and access it from an Electron window.

[0]: https://lispcookbook.github.io/cl-cookbook/editor-support.ht...

[1]: https://github.com/Lisp-Stat/lisp-stat/

[2]: https://github.com/mdbergmann/cl-gserver

Is emacs no longer the go-to editing lisp?
It pretty much still is, but support (and connecting to a Lisp instance via SWANK) is improving in other editors.
Are there any schemes besides racket with interactive edebug style debugging support in emacs?
Does this have anything to do with a comedian called Zakariyyah Galifyanakkis?
If you want to do something like what the article is describing with Guile, but without the GPL constraints, you might like s7 Scheme. It's a bit like a cross between Guile and Janet, with CL macros, keywords, and first class environments. I use it for Scheme for Max, an extension to the Max/MSP computer music platform allowing one to script Max in Scheme. It's BSD licensed.

Also, the author Bill is very helpful on the mailing list.

s7: https://ccrma.stanford.edu/software/snd/snd/s7.html Scheme for Max: https://github.com/iainctduncan/scheme-for-max