then M-x slime-connect localhost 4005
BTW, here's a k8s-hosted app I wrote using this approach: https://github.com/atgreen/red-light-green-light
EDIT: off topic, sorry, but I have been actively evaluating CL (using an embedded web server that starts a browser) vs. Swift (using mostly SwiftUI) for a new product I want to write. I find myself using Swift like I would CL: using Playgrounds to prototype low level code and utilities, then XCode for developing the UI. To be honest, Swift and SwiftUI is a better fit technology-wise for what I want to do, but I am so much happier when working in CL.
This is not true IMO, when compared with the languages of today. Even if you include the numerous little details of MOP, the CL spec is nowhere near as confusing as say something like C++ 17.
Have you evaluated CAPI? Have you looked into building Cocoa applications with Clozure CL?
The advantage of Swift and SwiftUI is that my application can run on both a MacBook and iPad, sync data with iCloud.
The advantage of LispWorks is that I could fairly easily support both Mac and Windows (and Linux).
I spent two evenings last week playing with Clozure CL and its Cocoa support. It has low level APIs and I had a peculiar issue with sometimes not being enter text into input components.
EDIT: to be clear, in general I love Clozure CL. In this instance I was probably shooting myself in the foot, somehow, or maybe the issue was that I blew the install in some way getting it running on Catalina which took over an hour. The Catalina problems are hopefully just short term issues.
In particular, is object identity with EQ consistent with functional programming? Constructors do not act like functions if EQ is the equality. Or should that be more "immutable programming"?
Common Lisp, because it has EQ and object identity, cannot perform some optimizations that a truly functional language's implementation could. In particular, it cannot combine equivalent function calls, and cannot merge equivalent data (hash consing or the equivalent.)
I know that the functional universe has solutions to these problems, like the State monad and reusable collections protocols based on common minimal primitives, but I really appreciate being able to just freely string together tokens like DOLIST WHEN EXT:COLLECT without any elaborate frameworks.
So I suppose that programming in Common Lisp makes me appreciate non-functional programming.
Having said that, I do feel like a caveman every time I spend brain cycles on picking between EQ/EQL/EQUAL/EQUALP/STRING= or worrying about whether the object I'm updating might come from a quoted constant and invite undefined behaviour. Hard to have it all...
I wrote a wrapper for it and SERIES, called folio, followed by a revised version called folio2. If your requirements resemble mine when I wrote it, you might findit useful.
You can find it here:
https://github.com/mikelevins/folio2/A package I've been involved with lately is fset, which is available through quicklisp, or at
https://github.com/slburson/fset
It has some interesting features, including "functional setf expansion". This would turn something like
(setf (fcar x) y)
into something equivalent to
(setf x (cons y (fcdr x)))
(where "fcar" and "fcdr" are the same as "car" and "cdr", except when they are in a setf-able place form.)
(fcar and fcdr are not in fset; I used those names just for exposition here.)
It could work with nested accessors, but only if it bottoms out in a variable.
By the way, I found a lot of your best practices questionable to say the least. Since it's obvious that you are a newcomer to CL, I would refrain from producing "best practices" type blog posts until I had a few years of experience under my belt.
For that reason, I also found your post confusing and I'm inclined to categorize it as "mostly rehashing stuff that is already there" rather than strong signal. There is ample, good, introductory material for CL on the net, we should strive to think before we dilute it with derivative posts.
A year or so back I picked up a copy of Land of Lisp and burned through it for pleasure reading. And I was struck by how gross Lisp looks in that beginner-oriented treatment. Just this huge slog of car and cdr and let/letrec/let* and the 37 flavors of equals and the function namespace. . . and, all the while, you're being told that persevering in mastering this confusing minefield of subtleties will somehow enable you to write bug-free software. I doubt it's actually fun for most people, and the grandiose claims should beggar belief for everyone. I suppose I should count myself fortunate that I got to learn Lisp in college, where there was little attempt to make it fun, and plenty of graded assignments to keep me motivated.
Racket and Clojure are right to clean up some of the language's evolutionary history. That's a start. But even then, the treatment in beginner's guides isn't all that enticing. I've also skimmed through Realm of Racket and Clojure for the Brave and True, and, while both of them work hard at being entertaining (and were fun to just sit and read), they don't really succeed at dispelling the sensation that what you're mostly doing is wrangling with the language itself.
Compare with some of the more popular Python guides. They tend to be much more dryly written, but the actual flow of the guide tends to get you pretty quickly to
from pypi import have_fun
have_fun()The effort to get an editor going and to open a file and load a package (“system” in CL) is a lot. Of course, I’ll die on a hill claiming that those things are a constant overhead and aren’t even detectable when you wield lisp’s power.
Nothing inherently stops lisp from becoming simpler to start with and use, but most everybody who knows lisp has become not only competent with the current tooling, they’re happy with it. I’m happy too: I love using Emacs and SLIME. I love it better than VSCode, PyCharm, etc. I’m faster and more productive.
I wonder why other professions aren’t like programming. Adobe Premier isn’t exactly easy to use, yet professionals aren’t apparently clamoring for an iMovie equivalent.
Interesting. Nobody is born knowing any computer language. Languages therefore are popular (among other things) in proportion to their ability to convert "those who don't know the language" into "those who know", and "those who know" into actual users. A gentle introduction to those who don't know anything is therefore a good start. I suspect that both Lisp and Haskell suffer from this.
There are languages for people who want to get things done, and languages for people who want the perfect language for getting things done. The problem is that definitions of perfection differ between people, so people who are searching for perfect languages wind up in a small niche of people who agree, separated from people next door whose idea of perfection is just slightly different.
In the meantime people whose focus is on getting things done go with a popular language which builds a critical mass of ways to get things done. And the availability of useful libraries makes them actually better to get stuff done with than languages that are theoretically nicer in some way.
This is, of course, a worse is better kind of argument. See https://www.dreamsongs.com/RiseOfWorseIsBetter.html for context on that.
It’s true. Importing flask in Python and starting a server takes about 1/2 as much code as in Lisp. Some extraordinarily routine stuff Python has down to a one-liner. A lot of people have also written more Python libraries, so chances are you’ll be able to cobble any random doodad together with a huge dependency tree. But after a certain very short prototyping period, I end up fighting Python’s terrible deployment, terrible efficiency, and slapdash language implementation when trying to build something robust.
That said, I disagree with some of the points you make:
> availability of useful libraries
Clojure is embedded in both JS runtimes and the JVM. Both feature massive ecosystems.
In terms of getting things done:
Languages like Python, JS and so on are fantastic at plumbing. But when it comes to modelling data-structures, manipulation and algorithms then they are far less expressive and productive than Clojure from my experience.
I personally think the main issue really is the learning curve. There are quite a number of things that 'suck' about Clojure initially:
1. In a Lisp you are essentially manipulating an AST rather than writing statement-based text. Initially this is cumbersome and taxing both while reading and writing programs.
2. In a functional Lisp like Clojure (and other FP languages) there is a wide variety of commonly used functions which are used to manipulate and compose data-structures and other functions. Especially when reading code this can be daunting.
3. Setting up a environment correctly for REPL driven development as a beginner and tuning it as an intermediate user is quite an undertaking in comparison to many other languages.
4. Clojure specifically being a hosted language forces you to understand the hosted ecosystem as well plus the wiring between the host and Clojure.
The payoff for all those four points is worth it:
1. Search for paredit visualizations like this one: http://danmidwood.com/content/2014/11/21/animated-paredit.ht.... Manipulating code this way requires mechanical prowess and exercise but scales up really nicely, especially alongside better understanding of Lisp code in general.
2. The incredible variety of functional building blocks scales really well with your experience and understanding. You are programming with a series of descriptive expressions, rather than lower level statements. Code becomes more declarative, dense and expressive. Abstraction is much more fluent.
3. A nicely set-up environment enables very fast development cycles and allows for understanding pieces of code of any scope in isolation, because you can evaluate, change and test any expression instantly.
4. This is in my experience a necessary evil to get higher adoption and a the massive library ecosystems of JS and Java.
I assume there are a lot of capable developers who simply cannot get over 1 possibly while being pushed back by 2-4. And I have to admit: If I wasn't already infected with Lisp at a younger age, I probably wouldn't have bothered with Clojure or any Lisp. But now it is my favorite for manipulating and modelling data AKA doing 'business-logic' and transformations between APIs.
Also: "Lisp is the most fun you can have programming, yeah from the start," while subjective, is by most accounts, wrong. Especially when referring to Common Lisp; there could hardly be a more convoluted Lisp than Common Lisp.
Based on the blog posts I've read, it seems like CL occupies the kind of space I want to be in: sort of the halfway point between theoretical and engineering. Is that a fair conclusion to draw?
(defun f (x) (* x x))
(setq g (compose f f))
(g 5)
This is wrong in Common Lisp on many levels: f must be referred to as #'f, and g cannot be called as such, you must use funcall: (funcall g 5)These might go against the sensibilities one might have had in learning a Lisp in the first place. But in practice, these don’t stop you from writing solid, readable code.
It won’t feel as “clean” or “academic” as Scheme, but you’ll feel it easier to build large and efficient programs without pulling your hair out.
(setf (fdefinition g) (compose f f))
Still different from Scheme, but some of the differences can be papered over with macros.Basic Lisp programming with lists is a lot cleaner in a Lisp in which the empty list is false, and in which accessing nonexistent parts of a list (including the empty list) is a safe no-op that yields nil.
Ashwin Ram's (cdr (assq key a-list)) almost works in Common Lisp in the form of (cdr (assoc key a-list)). See: https://ashwinram.org/1986/01/28/a-short-ballad-dedicated-to...
Imperative programming is better supported in Common Lisp because the evaluation of the arguments of most forms, including function calls, is ordered, mainly left to right, so side effect embedded in expressions will show stable, portable behavior. Scheme function calls have unspecified evaluation order, much like C. (In Common Lisp, the only unspecified aspect is whether the function cell is sampled before the arguments are evaluated, or just before the function is called. It's extremely rare for the arguments of a function call to be redefining the function cell, needless to say.)
Common Lisp forms return a predictable value. If it doesn't make sense for a form to return a value, its value is not "undefined", but either just nil or else "no values". When a value returns no values, and an attempt is made to use its value anyway, then nil is produced as the value. You will not see some annoying #<undefined> in a Lisp REPL coming from a procedural construct.
To answer your question, the JVM has wide acceptance in the industry and a big ecosystem of libraries, from a business and practical point of view, it seems the most adequate choice for a Lisp?
There also might be an issue of speed, since CL is compiled to the metal. Modern JIT JVMs probably have mostly eliminated CL's speed advantage, but I'd still be surprised if some things in SBCL (arguably the best-optimized CL) didn't run faster than in Clojure.
We can also use CL in Jupyter notebooks, see those good examples: https://gist.github.com/WetHat/a49e6f2140b401a190d45d31e052a...
And Vim, Lem, cl-repl, Eclipse (not so interactive)… https://lispcookbook.github.io/cl-cookbook/editor-support.ht...
As far as I can tell, it's more like you have to pick one or the other. Distribution is easy if you don't mind distributing a 50 MB image (which is, of course, trivial for some applications and catastrophic for others). Most of the suggestions on reducing application size are essentially accounting sleight-of-hand that doesn't actually reduce the overall footprint (I'm reminded of the compressor that can "compress" any file by one byte... by moving the byte to the file name).
EDIT: Based on a comment and downvote I suppose I didn't make my point clear above. I am saying that myths are not the only thing holding Lisp back. (I may be wrong. I hope I'm wrong. Big thank-you to people who are linking ways to get libraries in Lisp, especially Python libraries.)
I care 10x as much about libraries as about all these red herrings (EDIT: or myths, as you like) combined: " * Common Lisp does not have compile-time type checking. * Common Lisp is for imperative, object-oriented programming. * Common Lisp is too specialized, it’s not for general-purpose development. * Common Lisp applications are hard to deploy. "
Anyway I don't think the library ecosystem is so dire... Lots of good libraries are distributed through quicklisp. It's pretty straightforward to wrap a C library. (You can even do C++ easily if you switch CL implementations to Clasp...) If you need Java libraries, you can switch CL implementations again and use ABCL. (But of course all your CL code and CL libraries still work.) Lastly, for Python there's https://github.com/pinterface/burgled-batteries (and https://github.com/snmsts/burgled-batteries3 for py3) that even in an incomplete state might suit your particular library needs.
Perhaps this capability isn't very compelling since Python can also access C without trouble (and Java via Jython, .NET via IronPython)? Well I guess all I have left to ask is whether you've considered there might be libraries (or features) in Lisp that would be needed that don't have equivalents in Python? What do you do then? One possible library for admittedly niche applications that came to mind was a hierarchical task planner (https://github.com/shop-planner/shop3) but I forgot someone did indeed make a Python library (https://github.com/oubiwann/pyhop) based on an older version (SHOP1) of the background work, so depending on if you need the v3 features it might suffice.
No-doubt-unnecessary boilerplate: I understand that people have language wars, and it's possible you may have identified me as belonging to one side or another, but I'll pass. That whole state of affairs is just unfortunate.
(And a Numpy clone: https://numcl.github.io/numcl/)
https://run.nextjournalusercontent.com/kommen/parens-for-pol...
¿Por qué no los dos?
https://github.com/metawilm/cl-python
https://github.com/snmsts/burgled-batteries3
My point was that it is not just myths (or red herrings either) that are holding Lisp back. There are some issues based in reality as well.
> Common Lisp does not have compile-time type checking.
Nothing in the standard mentions compile time checking requirements and there is no useful de-facto standard that you (or tooling!) could seriously build upon either. I'd be suprised if python did not have better "compile-time type checking" for all practical purposes. Yes, SBCL gives much better type warnings at compile time than python, but for python you have mypy and it's A Thing, and still a joke compared to a proper type system like Ocaml's.
> Common Lisp is too specialized, it’s not for general-purpose development.
Common Lisp has no eco-system to speak of for machine learning, web development, command-line utilities, games programming, mobile development, GUI programming, embedded development or pretty much anything real general purpose languages do. If you have something for which Common Lisp is a good fit, you can still be commercially successful using it because not everything requires a super rich eco-system and for some problems what Common Lisp has is in fact highly competitive. But if you want to use common lisp effectively you definitely need to pre-select the problems you want to work on accordingly to an extent that's not true of python, C, C++, Rust, javascript, Go, Java, and half a dozen other languages.
> Common Lisp applications are hard to deploy.
No server or desktop or web-browser comes with common lisp pre-installed. Building common lisp is a pain, because packaging/ASDF is terrible and you don't easily get a nice and small statically linked executable out either. Compared to exactly what language is common lisp not hard to deploy?
>No server or desktop or web-browser comes with common lisp pre-installed. Building common lisp is a pain, because packaging/ASDF is terrible and you don't easily get a nice and small statically linked executable out either. Compared to exactly what language is common lisp not hard to deploy?
I'm not here to defend all of the flaws in CL, but I really enjoy the SBCL feature that allows you to compile to a binary. Yes it may not be as small as a pure C/C++/Rust executable, but it's very useful and I've used in various projects with great success.
My heart still goes out to Python but I was I could just as easily create a Python executable as I can with SBCL.
In my experience, deploying a CL app is muuuch less a pain than a Python one: you build a self-contained binary, you run it and voilà, you can access your web app from the internetz. The binary is ±20MB in size (compiler, debugger and all included). With Python, there are so many ways to fail (and even because a dependency of a library didn't pin its dependencies well enough. Gosh.).
Building a self-contained app is 1 line in the .asd project declaration.
https://lispcookbook.github.io/cl-cookbook/scripting.html#bu...
CLI utils: it's simple to use cl-readline. There are a couple libraries for ncurses. The Lem editor is a good example.
mobile: nope. For the adventurous, see https://gitlab.com/eql/EQL5-Android and its REPL.
GUI: https://github.com/CodyReichert/awesome-cl#gui Qt4, Gtk3, IUP, Tk, Nuklear have good bindings. Qt5 is possible with gobject-introspection (for the adventurous). Proprietary: CAPI. Electron: Ceramic. Java GUI interop with ABCL?
ML: https://github.com/CodyReichert/awesome-cl#machine-learning MGL's author won the Higgs Boson Machine Learning Challenge, but yeah.
for the rest: IDK https://lisp-lang.org/success/
GUI: would you develop a commercial app (to pay your rent) in any of these apart from CAPI?
ML: You are joking, right?
https://www.cliki.net/Machine%20Learning
> web development,
https://en.wikipedia.org/wiki/Viaweb
https://en.wikipedia.org/wiki/Reddit#Technology_and_design (though it admittedly got rewritten into Python)
> command-line utilities,
https://github.com/TeMPOraL/hju, among, like, hundreds of other examples
> games programming,
https://en.wikipedia.org/wiki/Game_Oriented_Assembly_Lisp
https://github.com/shirakumo/trial
https://borodust.org/projects/trivial-gamekit/getting-starte...
> mobile development,
http://kriyative.github.io/2011/03/26/ecl-for-ios-updated/
https://common-lisp.net/project/ecl/posts/Lisp-ECL-and-QML-Q...
> GUI programming,
https://common-lisp.net/project/mcclim/
> embedded development
http://www.ulisp.com/ (not technically Common Lisp per se AFAICT, but seems to have very similar semantics)
> Building common lisp is a pain, because packaging/ASDF is terrible and you don't easily get a nice and small statically linked executable out either.
https://www.xach.com/lisp/buildapp/
http://www.sbcl.org/manual/#index-save_002dlisp_002dand_002d...
[] For the record CLIM had interesting ideas, but last I looked, calling McCLIM's implementation of them a toy would be charitable.
That said, I support free software and I believe a proprietary compiler is not a good idea :(
A lot also provide you the source code once you pay. AllegroCL is one of them I think.
https://github.com/linpengcheng/PurefunctionPipelineDataflow
The article recommends SBCL, which does support TCO.
An old (2011) survey of TCO support is at https://0branch.com/notes/tco-cl.html
SBCL supports TCO: see http://www.sbcl.org/manual/#index-Tail-recursion
One can drive whether SBCL optimizes tail calls by setting the proper optimization settings: see http://www.sbcl.org/manual/#Debugger-Policy-Control
Haskell uses a different (data-centric, non-strict) evaluation model where recursive definitions don't result in recursive calls, so traditional TCO isn't as relevant. Recursion is used very heavily in Haskell—which has no first-class looping constructs—but the resulting programs generally do not require large stacks. It's not unusual to be able to run even large Haskell programs with a 1 KiB maximum stack size (+RTS -K1k). Space leaks are possible, of course, but they take place in the heap.
The point of the article is that while the Common Lisp standard is not really focused on functional programming, nothing prevents the implementions (and libraries) of today to be so.