Gerbil Scheme – A Lisp for the 21st Century
cons.io
cons.io
Personally, in practice, I rarely feel that resumable exceptions are important. Typically I have a data literal as a kind of mock and push that through the function or function chain I'm tinkering with in the REPL until I'm satisfied with the result.
Might be that I spend a lot of time in PHP/Psysh, Elixir, Picolisp and so on rather than good old CL, maybe I'd have another opinion if I had ever been payed for CL work.
(also kuddos to anyone using Factor in production^^)
In work settings I've mainly used Factor for data exploration but I can see it fit neatly as a data transformer or combiner in a pipeline from one set of API:s or protocols to some other, since it's relatively easy to express such things and one can get a performant binary to deploy.
https://www.theregister.com/2023/11/23/medley_interlisp_revi...
I'd love to see some kind of shim to run Medley on top of SBCL, rather than its own VM.
If you are trying to attract people to your programming language in 2024, I'm afraid that Emacs is not tempting bait, no matter how good the integration. It appeals to very niche tastes.
2. I've been trying to learn Emacs on and off for about 25 years and I personally think it's horrible beyond belief. Vim is just as bad, but at least as someone who's been using vi for closer to 35 years now, I know how to open something, change a few characters and save it.
I'm not a programmer. I don't use code editors from choice. But I have done in the past and I have opinions about them.
It looks to me like you have a choice: a nice modern editor with poor integration, or a horrible crufty old editor which doesn't even know what the Alt key or windows are called but which has great integration.
IMHO, as expressed on HN before, Lisp ought to have a slick GUI system for newbies approaching it. The commercial ones do. For FOSS, the code is out there. It's a done thing, many decades ago. It just needs modernising.
> For FOSS, the code is out there. It's a done thing, many decades ago. It just needs modernising.
That one is an ancient code base dating back to the 60s, written in a Lisp dialect roughly ten people in the world can program with some competence (I can't, can you?). It's a nice project, but it is far far away from what GNU Emacs provides. GNU Emacs has many many thousand man years more work and polishing in it, ongoing.
If you would learn a complex and large Lisp like Interlisp or Common Lisp, then one would better use a development environment for it. I doubt Medley is easier to use&learn than something like GNU Emacs + Slime + SBCL. For Common Lisp, Medley is the worse development environment, since very few code has developed for and with it. People use other tools to write Lisp code (vim, GNU Emacs, ...). People are just trying to get Medley to somewhat support the state of Common Lisp from 34 years ago (CLtL2). It has, like, zero support for Scheme or other programming languages (Python, Java, JavaScript, C++, Rust, ...).
GNU Emacs is not my favorite Lisp IDE, but it's by far the most practical solution for average developers: it does not cost anything, it has good Lisp support for several Lisp dialects and all kinds of other programming languages, it runs good enough on almost all current operating systems, hopefully respects my data privacy, ...
Sure one can use a new editor with less support for Lisp. I've also known Lisp developers using vi in a terminal. People have different preferences for their tools.
Believe it, baby.
See my response immediately above. Or maybe immediately below, depending on HN.
P.S. A macOS version is cheating. That I can use... but it's not the standard Emacs UI.
There is no such thing as a single standard UI. In addition to the terminal version, GNU Emacs was ported to window systems loooooooong time ago.
It's perfectly normal to use them. I use the GTK version of GNU Emacs on Linux.
Yes, there is, and it is called CUA.
https://en.wikipedia.org/wiki/IBM_Common_User_Access
I learned about 20 different text editors before CUA came along. I refuse to learn any new ones unless they comply with CUA.
I write English, not code. Any one of dozens of CUA editors are perfectly fine and adequate for my needs.
> GNU Emacs was ported to window systems loooooooong time ago.
So its programmers have had decades to adapt it so that it conforms to the standards set in the 1980s and adopted by the entire computer industry, from Apple to Xerox.
If they can't be bothered to adapt it, I can't be bothered to learn their ugly pre-standardisation UI.
Some examples: a document is held in a window which can be split into panes to see more than one at a time. Those are the terms. Use them, or GTFO.
The keystrokes for file operations are always Ctrl + the first letter of the relevant English verb, e.g. Ctrl+O is Open, Ctrl+P is Print. The Control key is abbreviated Ctrl and nothing else.
The key next to Ctrl is called Alternate, abbreviated Alt. It is not called anything else on PC systems, although Macs call it Option, abbreviated Opt.
It is not called "Meta" and no hardware made in 40 years called it that. The software was ported to the hardware; now port its manual and its UI, or GTFO.
Incidentally, a second danger of using the wrong name is that because it's wrong, different teams use it for different things. The KDE team call the Super key "Meta".
https://docs.kde.org/stable5/en/khelpcenter/fundamentals/kbd...
They are wrong, too.
Ctrl, Alt, Win/Super. Deal with it. The world changes. Software must adapt.
None of those use CUA (-> the Apple UI guidelines for those are either older (Mac) or newer), keystroke for file applications don't use control, there are no win/super keys, there are no multiple documents using window "panes" (as you can see in any of Apple's applications, for example Apple Terminal uses Tabs, not panes), ...
See: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts
Ugly...
Command is Super. Came key, same scancode, etc. Just a different name.
Very broadly, if one remaps the bottom left keyboard key on a Mac to Cmd, and the Super key to Ctrl, then CUA control keys all Just Work™ on Mac OS X.
This is in fact how I normally use Macs when using a PC keyboard on them.
So, no, I disagree on all points. (I don't really care about the phones. I don't own them or use them.) I've spent considerable time over decades studying this and I disagree with every point here.
Even for Microsoft: many users will now use something like the Microsoft Office suite in a web browser.
Take the current "Apple Pages" text editor / layout program.
It does not look and feel a bit like CUA. Not 10% of the UI works like in Microsoft Word...
For our entertainment: On my MacBook Pro keyboard I mapped the Lisp Machine keys:
control = control, meta = option, symbol = command, right super = right command, right hyper = right option
So I get from left to right: control, meta, option, super, hyper.
I use it, even though it is not CUA compatible. :-)
So then it does exist. Otherwise it wouldn't be called anything any more.
It's like bolting pedals on the sides of a huge car and saying "Look -- it's a bicycle now!"
CUA compliance needs a total rewrite of the UI and the manuals and the tutorial.
Fortunately, much of it has been done, but it's still incomplete and it's hard for non-Emacs-users to get working.
But it's here:
But I seem to remember after these 30 years that was something simple, like Ctrl-X Ctrl-S to save, and Ctrl-X Ctrl-C to quit.
Edit: I don't have Emacs installed absolutely anywhere; but I went to a machine provided by the GCC Farm project and tried it there. I remembered right.
The point being that I learned literally dozens of editors and UIs in the 1980s and early 1990s... and then CUA came along and swept it all away, and I never looked back.
I wrote much of the original Wikipedia article on CUA:
https://en.wikipedia.org/wiki/IBM_Common_User_Access
Much more recently I also wrote this:
https://www.theregister.com/2024/01/24/rise_and_fall_of_cua/
I never ran Emacs, never had any reason to learn Emacs, so I never did.
I did learn WordStar, WordStar 2000 (totally different), WordStar Express (totally different again), WordPerfect, MultiMate, DisplayWrite, MS Word 4.x and the different 5.x and the different 6.x, plus WinWord 1 and the different 2, and LocoScript, and The Last Word, and Edlin, and DOS EDIT, and many many more.
I am very much not unable to learn new editors. But for over 30 years now, I haven't had to. And that's wonderful and I am absolutely not learning another new different editor now, at over half a century old.
Any editor that wants me to try it must conform 100% to the CUA standard, or it can die in a fire. Weird editor keystrokes are 1970s/1980s stuff, and keeping them 40 years is inexcusable.
The core of vi is standardized in POSIX (IEEE 1003.1)
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/v...
The nice thing about standards ...
Maybe CL's flavor of image based development is just an example of good ergonomics, because the fundamental interactions (edit, compile, update image, test, repeat) are not really different from developing a C program inside a Docker container.
When coupled with hot reload, the experience comes close enough.
There's a new condition/restart package in the ecosystem, but this is currently of limited use because errors don't use it. However, one can use Rebugger to capture stacktraces and drop into an interactive debugger at the stack frame where the error is thrown (caveats mainly involve C 'builtins'), which is for REPL use the main advantage of condition/restart. I don't bother since I have InteractiveErrors installed, which lets me navigate to a stack frame in the error and open the editor at that line.
None of this is image-based, either, but on a level playing field the interactive programming experience Julia offers is exceptional.
If you want the list to be expandable without ever reindexing AND you want it to still be relatively compact, you can use Rosenberg-Strong which creates a square shells instead of Cantor’s diagonal shell.
https://arxiv.org/abs/1706.04129
Bonus content: Szudzik shows how Rosenberg-Strong lets you create a bijection from positive to binary trees ordered in a way so that larger integers never yield a tree of height N until it’s exhausted all tree instances of height N-1.
It's not necessary though, your first statement is plainly false as written.
I bet your link has some interesting algorithm in it. Luckily for Cantor he died before every having to participate in a software engineering interview.
A Lisp implementation as it should be.
At a glance it seems you could do a lot in systems and network programming, but if you want to produce a lot of PDF/A or run a cryptocoin business maybe use something else?
Why not write one myself? Drained. Lisp is near and dear to my heart, but midnight when I have to do our morning time routine with our 7mo at 6am isn’t the time to wax poetic.
The most worrisome thing is actually the most ironic: no one is posting any hate comments either. Back a decade ago, you’d get into long battles about whether it was a good or bad idea to choose any Lisp, or at least some jeers from the sidelines. But that, counterintuitively, was one of Lisp’s strengths. When I was younger, that kind of thing originally sparked my curiosity: if most people were jeering, and most people weren’t too smart, then could there be something to this whole Lisp trend (even if it seemed like more of a university project than a trend)? And it turns out there was.
Loved or hated, you’re noticed. The pit of indifference is a bad place for an ecosystem to be. It’s long past time someone write about it in… well, in a way that can only be described as pg style. He had a knack for making young devs hungry for more. https://paulgraham.com/avg.html
The essays still hold up. They’re as true today as they were then. But no one will believe them without an example. I wouldn’t have believed it if pg hadn’t released Arc, and sometimes it feels like I’m the only one in the world that actually uses it to solve problems that I personally have.
But the nice thing about Lisp is that it’s always there, waiting to be discovered. It’s arguably one of the few types of programming ecosystems that can be discovered rather than designed. It’s precisely why there are so many choices. And it also doesn’t matter that nobody else uses it, just that you like it, that it solves your problems, and you find it endlessly fascinating. Hopefully our generation won’t be the last, at least for a long while.
Every time I can’t delete the first element of a list in lisp (I.e. del x[0] in the python sense) I get annoyed with racket.
The reason I look past it is because the benefits are so good that they outweigh the annoyances. I wouldn’t trade it away.
$ pil +
: (cdr '(1 2 3))
-> (2 3)
:(Sorry for not being clearer to begin with. I realized as I was falling asleep that it would cause some confusion.)
Hmm, maybe a marriage between Gerbil and J -> Jerbil!
I've been a fan of Gambit for a long time too. Love to see it being utilized this way.
Isn’t this just the difference between mutable and persistent data structures?
x = [42, 99]
y = x
del x[0]
y[0] ; 99
This is impossible in traditional Lisp, and it’s the source of many of my frustrations. It’s why you can write an algorithm to destructively modify a list at any point — except index zero. The head of the list is the thing that’s used as a reference to the list. This doesn’t happen in Python; it’s why you can have empty arrays set to x and y, push a value onto x, and see it at y.Most of the time I try to get rid of variable names so it seems somewhat strange to invent more than one pointing to the same data structure in RAM, but I'm a simple person that gets confused easily.
(defun vector-delete! (vector index)
"Delete the element at INDEX from the given VECTOR.
Destructively modifies the vector.
The vector must have a fill-pointer.
This is not thread-safe, use at your own risk.
Examples:
(let* ((x (make-array 4 :fill-pointer 4 :initial-contents '(1 2 3 4)))
(y x))
(vector-delete! x 0)
y)
;; => #(2 3 4)"
(declare (type (Integer 0 *) index)
(type Vector vector))
(assert (array-has-fill-pointer-p vector))
(let ((l (length vector)))
(assert (< index l))
(let ((removed-element (aref vector index)))
(loop :for i :from (1+ index) :below l
:do
(setf (aref vector (1- i))
(aref vector i)))
(decf (fill-pointer vector))
removed-element)))
(Granted, it could be done in 6 lines of code, but I'm already cringing at not providing a continuation when calling it with an out-of-bounds index)I think there is less confusion about this kind of thing with Clojure, where a preference for immutable persistent data structures is strongly stated in the language design. So the persistent nature of lists and other data structures is fore grounded, and there is no confusion over why the equivalent of "del x[0]" is not supported.
I noticed this about Norvig's toy Lispy:
It implements cdr as mylist[1:]
So if you recurse on a list with cdr, then you will get mylist[1:] mylist[2:] ... mylist[n:], which is a quadratic algortihm
Ordered data types are core to computers actually: arrays, heck even the memory itself. I always thought the list to be the most fundamental construct, but outside the neat world of computer, the real world is mostly unordered. Order is artificial, is indeed often not a given.
Has this been actually true in the last, say, two decades? Or is just a thin veneer to make your highly concurrent machine look like a PDP-11? Right now order seems to be somewhat important only when it comes to cache optimization: if you stick thing that belong together close to each other you end up with fewer cache misses.
My personal static site generator (https://hg.sr.ht/~zck/site-generator, generating https://zck.org/ and https://theflyingbuffalo.com/) is written in Arc. It's a very fun place to program.
I've used other Lisps, but Arc for me fits a very enjoyable programming space. There's a small enough core that I don't have to spend a lot of time figuring out, say, how to change logging levels or why Spring isn't parsing a POST body into the object it's supposed to be. But it's also large enough that I don't need to rewrite an http handler.
Run it like `(gensite "path/to/site/base")`. The site root is a folder with the following things in it:
1. a config file `conf.arc`, with config for the site. It has these keys (but I don't think they're all required):
1. sitename -- title for the homepage
2. site root -- url; mine is "https://zck.org"
3. description -- used in the RSS feed.
4. slogan -- used in the sidebar
5. navbar-entries-at-top -- extra links at the top, as a hashtable. Mine is ((tagged table ((href "art") (content "my art"))))
6. mailing-list-cta -- A thing put at the bottom of each page (except the homepage) that is a call-to-action for my mailing list.
2. a folder called `published`. This contains the entries in the site. Each file is the html contents of the body of the entry, and I don't believe it matters what you call the file. Each entry also has a bonus set of options in it, which I'll explain later.3. a folder `pregenerated` that has things in it copied verbatim over to the output. I use it for things like my favicon, css, images, and some html games (https://zck.org/numberdle/?variant=rationerdle).
4. a folder `frontpage`. This contains entries that do not directly result in a page; they merely result in entries placed on the frontpage and rss feed. They have the same options that the published entries do. I use it for things like announcing when I've given a talk, or launched something that's in `pregenerated`. This can be empty; it just won't generate any entries like this.
5. a folder `codegen`. This folder contains files that contain arc code. Each file, when executed, results in a list of pages. There's some busy work in passing args into it, and getting the results back. Don't start by using this; like `frontpage`, it can be empty, or maybe even nonexistent.
Now, the important part of each page entry -- the "options". This is a serialized arc obj that is put at the top of each entry. It has keys `url-slug`, `title`, `date` (in YYYY-MM-DD format), `navbar` (set it to t to put this entry in the navbar), `frontpage` (the string that gets put in the frontpage for this entry; an <a> link with no href gets set to link to the entry), and `tags` (a list of strings that tag that entry; see https://zck.org/tags).
So my latest entry (https://zck.org/ruby-hashmap-syntax) looks like:
``` file begins
((url-slug ruby-hashmap-syntax) (title "til: Ruby's hashmap syntax") (date "2024-03-20") (navbar t) (frontpage "Ruby's hash syntax is confusing. There are two different syntaxes, and they <a>work surprisingly differently</a>.") (tags ("til" "ruby")))
<div> <p>I was writing some Ruby for the first time, and I made a hashmap with some data in it.</p>
```rest of file snipped
The arc hashtable is not printed as part of the html output; it's only used for its values, and the page itself starts after it. In this case, the page starts at the <div> tag.
I write my blog in Emacs org files, for which I've also written my own org-mode exporter (https://hg.sr.ht/~zck/ox-zhtml). I've done this because the built-in html exporter creates some really ugly html. You shouldn't be able to tell what html exporter created your html; the org one puts in a lot of "org" properties that are terrible.
I'd love to know if you get this working! Feel free to email if you want to talk off-thread or get more help. I'm sure there is more I missed.
Source: https://en.wikipedia.org/wiki/Gambit_(Scheme_implementation)
https://cons.io/guide/intro.html
Seeing it has objects, methods structs, optional type annotation, couroutines, actor model, I tried to build it but the Gambit Scheme on my system was too old and I gave up.
Also an older but still useful overview of all the Schemes when used for scientific computing:
Compiling a newer Gambit from source is really straightforward. I ran compiles of Gambit over and over to stress-test Raspberry Pis to see if they needed active cooling (they did).
I see a hello world. I see some obscure (to me) syntax. I see talk about how this will solve world peace.
But never "here is http.c and here is http.$newlange" side by side, this is how they perform, these are libraries and these are language functions.
I personally want to see a non-marketing-bullshit statement of why other languages suck and why the author(s) feel that this new language doesn't (at least for some given domain or style). I obviously don't want the statement to be insulting or anything, but give me something real. Every new language claims to be "safe, ergonomic, performant, easy-to-learn, beautiful, scalable, cloud-integrated, AI-powered, "heart" open source, etc." I want a language that says, "Hey, other languages get X wrong and we don't. If you don't like it, that's great- go back to those other languages." where "X" could be error handling, performance, type systems, whatever.
Sadly, most LISP and Scheme implementations spend fourteen pages of README material explaining why they reinvented a particular wheel instead of, you know, putting a simple trolley together. HTTP implementations in most LISP/Schemes are particularly bad/abstruse, to the point where I just give up if I can’t do a simple HTTPS request with bearer auth and a JSON payload inside of 30 minutes.
Serving HTTP… oh boy, don’t get me started.
Lately, I'm going back to what I used years ago: Common Lisp. There are great books describing it, and implementations like SBCL https://www.sbcl.org/ are solid.
p.s. Plenty of time to come with the lisp for the 22nd century!
I got pretty into Chicken Scheme a few years ago, and I did a quick breakout clone in Racket as well, and I think they're pretty cool, but Clojure is the only one that has ever evolved past "toy" for me.
Do you ever need it?
The Apache tools are the biggest things for me; I do a lot of work with Apache Kafka, so being able to directly use the first-party libraries like Kafka Streams is useful. If I need to do any kind of distributed processing stuff, I have Apache Spark or Apache Flink when I need it. It's kind of falling out of favor now, but I still occasionally have a need for Apache Zookeeper as well.
Now, I'm sure that there's Clojure-first versions of these things, but the sad truth of the software landscape for me is that a lot of it is still powered by Java. I can either be tasked with reinventing a lot of the infrastructure myself by using a language that doesn't have good Java interop, or I can use a JVM language. I think that Clojure is the least-bad of the latter category.
In some ways, Clojure is an even better Java than Java; it's easy to compose together arbitrary java methods, without any kind of fancy fluent interface or anything, for example, and there's lots of helper macros that I think really do smooth over the Java-ness of certain interfaces.
I bring this up, because I am curious which “Clojurisms” annoy you? I probably didn’t notice them because I wasn’t used to other lisps when learning it.
A lot of Clojure people might be like "I don't see anything wrong with it, in fact I prefer things the Clojure way." Whatever. You do you, man. I just can't with that. Kawa Scheme for me every time if I want to Lisp with Java libraries. Second place, ABCL.
The discussion about parenthesis is a bit ridiculous, but unfortunely their visual distribution does matter, and Clojure is more appealing to folks without Lisp/Scheme background.
thanks for explaining.
If my goal were as simple "read and write and commit to Kafka", the vanilla clients like rdkafka would be fine. However, most of my personal projects for the last year have made pretty liberal use of the Kafka Streams library, which as far as I'm aware only exists in JVM land.
We've used it and it worked well.
Newlisp's default (and most used) FFI is also... dangerous. You simply import a symbol from a shared lib and it gets bound to a procedure corresponding to a C call to that shared lib. No specifying of parameter types -- and most platforms do not encode parameter type information in shared libraries in a standard universal way. (Things like C++ name mangling, and COM, only apply to libraries written within their respective ecosystems.) But don't worry. Newlisp trusts you to get the parameter types right, because Newlisp is for the practical Lisp programmer. Woe betide you if you don't, though!
Guile's (system foreign) module provides a similarly dangerous FFI, but at least you can (and must) specify parameter and return types when you import a foreign procedure from a shared lib that way, allowing for checks for parameter correctness at the call site, if not the import site.
If they each make it a Lisp: oh no, how many Lisps do we need?
There's nothing wrong with that, but , oh, look, a new scheme, yea. None of the previous 70 changed my life in any meaningful way.
Functional programming is easily available in almost any modern lang, virtually all of which have better tooling (and no, I have zero interest in using emacs) and ecosystems.
a bit of googling also came up with https://www.quora.com/How-has-Scheme-been-used-in-real-world...
> virtually all of which have better tooling (and no, I have zero interest in using emacs) and ecosystems
Anyway, there are a lot of Lisp-like (for a given value of like) language implementations indeed, but then there are also a lot of recordings of various orchestras performing Beethoven's 6th Symphony.
Someone making just a RNRS Scheme, with nothing new in it compared to other Schemes is at least honest.
Tcl/Tk is still an unrivalled way to prototype a GUI -- and with Snit megawidgets, it's almost like programming a modern JavaScript component framework like React.
Completely agree about the utility of Tk. I remember reading somewhere that HTML/CSS widgets were influenced by Tk. Don't know how true that is but I can see how it could be.
(I actually tried to retrofit some of CL in Tcl, heh; https://wiki.tcl-lang.org/page/q3cpma)
I think we are still just babies in the history of programming. 50 years is nothing.
It sounds like the Discordian battle cry: united we fall, divided we stand!
One Lisp to rule them all, One Lisp to find them, One Lisp to bring them all, and in the darkness bind them.
We don't have top-level terms that cover Java, C# and Kotlin, or TypeScript and JS, or C/Objective-C/C++.
int main() { }
\begin{document}
cp ${srcdir}/x ${tgtdir}/y
.foo p { border: 0px; }
/m { newpath moveto } def2) JavaScript family languages
3) C family languages