Extending Emacs with Fennel (2024)
andreyor.st
andreyor.st
FWIW there are blog posts from the same author of the Emacs setup: https://andreyor.st/tags/game1/
Can't say I made anything worth mentioning. There are some bigger templates available that I am sure do more useful things, but I prefer something small enough that I can see what is going on.
Worked fine even for getting things to run in LoveDOS, a port of some older Love2D version to MS-DOS. In practice compilation was a bit too slow for comfort, so a better way was to pre-compile the fennel-scripts to Lua and just run those.
I installed some LSP server for fennel that comes with optional built-in code completion for both Love2D and TIC-80. Works well in emacs.
https://github.com/yfuna/paip-el
The original one:
https://github.com/norvig/paip-lisp
Paradigms of AI Programming:
https://upload.wikimedia.org/wikipedia/commons/d/d4/Peter_No...
I would add Clojure to the modern Lisps, too, which I find the most feature rich (even though had much more blast writing Janet).
Yup. Clojure may not be the most lispy Lisp but it reaches: it works on top of the JVM (with super easy Java interop), it transpiles to JavaScript and I also use it to write script using Babashka (GraalVM/AOT native Clojure interpreter which starts in milliseconds, avoiding JVM startup time [notoriously slow for regular Clojure programs]).
Being able to share code between Clojure and ClojureScript is really sweet.
Clojure(Script) / Emacs (CIDER) / eglot (LSP client for Emacs): life is good!
(dict :a 1 :b 2)
(it's an 'equal HT)With toggle-pretty-print-hash-table it will pretty-print its content, readably.
(macroexpand '(when-let (foo (frob bar))
(jib foo)))
;; (let ((foo (frob bar)))
;; (when foo
;; (jib foo)))Racket improves on Scheme: its macros are fully hygienic whilst not being limited to template -> pattern transforms. See https://docs.racket-lang.org/guide/macro-transformers.html
EDIT: syntax-case -> syntax-rules; R6RS specifies the latter—I believe the former is a Racket construct equivalent in power to `syntax-rules`.
It does not have the same power as `defmacro`: you cannot define general procedural macros with `syntax-rules`, as you are limited to the pattern-matching language to compute over and construct syntax objects.
[1]: https://docs.racket-lang.org/reference/stx-patterns.html#%28...
R6 having `syntax-case` macros is one of the more controversial things about it; a surprising number of implementers don't care for them.
Scheme R5RS, R6RS and R7RS all have macro systems. In R5RS has a pattern/template based system (syntax-rules). In R6RS the system has both patterns, templates and procedural macros.
The most modern system is Racket though. See `syntax-parse`.
Wait, r6rs has proc macros? Where is that in the spec? I've only seen the `syntax-rules` bit.
Look for `define-syntax`.
https://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_i... https://www.r6rs.org/final/html/r6rs/r6rs-Z-H-13.html
As far as I know, the CL spec hasn’t been updated for 30+ years, and most of its design is far older.
(defmethod join ((a String) (b String))
(concatenate 'String a b))
;; example: (join "abc" "def") => "abcdef"
(defmethod join ((a Integer) (b Integer))
(parse-integer (format nil "~D~D" a b)))
;; example: (join 123 456) => 123456
And rudimentary support for ADTs: (deftype Low-Score ()
'(Integer 0 20))
(deftype Normal-Score ()
'(Integer 21 79))
(deftype High-Score ()
'(Integer 80 100))
(deftype Score ()
'(or Low-Score Normal-Score High-Score))
(But note that deftypes aren't allowed to recurse.)CL also has first-class support for debugging with things like describe, step, and trace built-in.
EDIT: Yeah, the CL spec dates from 1994 and a bunch of things which we would expect nowadays (networking, POSIX,...) are provided by external libraries rather than being part of the spec, but in various ways CL is way ahead of its time.
Which goes to show how many lessons the industry failed to learn on those 30+ years.
CL has a more-or-less frozen standard, in the sense that it's unlikely to have an update. Scheme gets updated standards, but they seem to focus on refining Scheme rather than adding "modern" features. Both are very extensible and people do add modern features as implementation extras or libraries.
I can't comment about Racket. As an outsider, it appears to be a playground for hardcore CS types to experiment with different programming language features, which suggests it's the most "modern." That's just the impression I get, though - feel free to correct me on that.
Typo, it should be: “with one n” (as the earlier commenter wrote “Jannet”). It took me a while to parse this, I was searching for the nonexistent “l” embarrassingly long.
Janet is also very good, I like it a lot I just don't have a ton of use for it. It's good as a high level scripting or glue language, but then so also are ruby, python, node, elixir, raku, and so on you probably already know several so it's hard to justify learning another. It's pleasant though.
I think janet is actually an extremely strong lua competitor for embedding, it takes the best parts of lua's design on that front but also includes excellent tools for implementing parsers and DSLs (PEG lib in the language core! and macros) which is most of the time what you're embedding lua for in the first place.
So basically janet is good to replace lua, and fennel is good when you can't replace lua.
Re: lisp. I think the main thing about "modern" lisps is that they don't use a cons cell or linked list as the main data structure, though they still use it for code structure. These two (and also clojure) use hashmaps and arrays a lot more, and have lispy ways of interacting with them. Some nerds will say it's not really a lisp for this reason and to them I say "ok."