Between two Lisps
ane.github.io
ane.github.io
But once you realize that CL has more than two namespaces (I think it has at least nine in the language itself, unlimited in user programs) it's easier to understand how it can be more than historical accident. People are used to thinking words in context.
I do mostly numerical programming today. Common Lisp would have been my preference and IMHO much better than Python I have no time to rewrite numpy or R libraries in CL.
I think method combinations and packages are other namespaces.
As for compiler macros, they do have their separate namespace, accessible via the `compiler-macro-function` function. They can be invoked instead of functions, but they exist completely on their own.
It's been done. I have no experience with it but a numpy clone in CL exists.
Ignoring API compatibility and speed etc. (numcl doesn't talk to BLAS/LAPACK IIRC), even the basic datastructure isn't quite there. Numcl uses fixed contiguous lisp arrays as opposed to the strided ones that are standard in Numpy.
P.S- There are atleast 5-6 attempts to build something of the sort starting all the way back from Matlisp, and none of them are feature complete.
The lisp curse is very much true it'd seem.
With Julia I can almost pretend I’m using a straight-up lisp. The multiple dispatch in Julia is quite nice and is sort of “the big idea” that Julia uses to be so magical.
However there is OpenDylan for anyone that would like a go at it, including the old book describing the language.
I feel like Julia is pretty great, and has a good amount of inertia behind it in the domains that I frequent, but I’m curious about (Open)Dylan from the perspective of someone who’s used both.
> The parameterising complicates the subtyping relationships needed to specify how multiple dispatch works.
I don't think it adds all that much complication actually, and it adds a ton of extra expressive power. For instance, our arrays are parameterized by their element type and their dimensionality, so
Array{Int, 1}
is a 1D (vector) array of Ints whereas Array{Complex{Float64}, 2}
is a 2D (matrix) of Complex Float64s. This is really important for making the most of multiple dispatch imo because I can write methods which take advantage of as much type information as possible. This isn't just helpful for performance, but also helpful for just expressing your algorithm in a clear and general way.Lambda calculus, list manipulation, list operations, closures, these are concepts I learned twenty years ago learning Scheme, and I‘ve seen these concepts slowly be adopted in other popular languages decades later.
Thank you for catching my interest in Scheme again! Will look into that in more detail. Was an old love of mine anyway. :)
The big problem is Clojure is a language without a published standard.
My first programming language was Perl. After Perl came a little bit of C, but then I soon switched to Common Lisp. I wrote a lot of Common Lisp: I programed a text-based adventure game engine ala Zork entirely in CL! After reading Paul Graham's On Lisp and learning about Scheme, I took a look. My dad got me a PDF of SICP; I learned to prefer the Lisp-1 style of Scheme to the Lisp-2 style of CL.
I've done a bit of Clojure—I feel like Clojure does a better job of making data structures more ergonomic than either Scheme or CL.
I've recently started working more in Racket and I am loving it. It feels like a Scheme with a more visible, vibrant ecosystem. (Could just be me not being aware of the Scheme ecosystem—I was only 15 or so at the time I started writing Scheme.) Racket is definitely optimized for building DSLs. But I feel like the standard library is a bit better documented and easier to work with.
Just my experience though. I could be totally missing out on some great Scheme libs.
At the end of the day, Lisp in all its flavors is great, and I want to write as much of it as I can in my career.
There's been a couple decades' worth of divergent evolution since then, so that it's not actually compatible with any Scheme standard anymore. At least not by default -- there's still "#lang r5rs". In any case, one could be forgiven for thinking Racket is still just another flavor of Scheme.
At the end of the day, language choice, much like choice of editors or mechanical keyboards, is not that important — what is important is actually coding and building useful software. This can be done in any language, although I do not want to sound ignorant, as certain languages are better suited for certain tasks (eg swift for iOS dev).
As a Mac/Windows user, I’d be keen to know how popular Guile is for GNU Projects?
For those who want to learn Common Lisp quickly, this is a great guide: https://github.com/ashok-khanna/common-lisp-by-example
A spec entry for delimited continuations with call/cc as an -optional- feature would definitely seem like an improvement in terms of leading implementors down a saner path.
For a better understanding, https://en.wikipedia.org/wiki/Delimited_continuation didn't feel like a terrible start and I'm far from an expert here.
It contains a very telling comparison about delimited vs undelimited continuations and sows quite clearly by you always want delimited ones.
Can you elaborate on what it is about CL syntax that you prefer over Scheme, and why?
It's just a convention that you're free to ignore in your own code.
CFFI
I'm curious to see what happens with the C++ support in claw. They're working on it, but I'm skeptical. I haven't had much luck using arbitrary C++ libraries from any language other than C++.
And I don't know what the author means by "somewhat rarer" in this context, but I personally use C libraries from CL all the time. Almost every CL project I work on, now that I think about it.
That said, claw's C++ support is still in the works and I don't recommend diving into that mess yet. Stay tuned for Q1 2021 semi-usable alpha release. Or not. C++ is such a pile of shinies, I'm pretty sure I lost a few screws during :claw development.
Very surprised to see GLM in that list!
Guile has a C API in libguile, so you can create C that's directly callable from Scheme with little overhead. You can write Scheme procedures in C and you don't need to gobthrough FFI.
As far as I know both languages have FFI. CL has CFFI, Guile has it in its standard library.
But can I actually ship some C code with my CL system and have ASDF take care of building it? If I want to ship CL bindings for a C library, as far as I've understood I need to tell users to install the library first. Maybe something like Clasp offers a C++ API?
Then again in both languages you can always use FFI and if things get hairy you can use grovelling.
With Guile, it's really easy to have a C library and package its guile bindings in the same place. Not saying you can't do this in CL, just that I don't know what it would look like to ship both C and CL in the same package.
(author)
Guile is an extended specific implementation of Scheme. Common Lisp OTOH is a language standard with widely different implementations. Just like Scheme has very different implementations - in sizes of small, medium and large.
Common Lisp with more extensive C extension support are CLISP (written in C, not that well supported nowadays), ECL ('embeddable Common Lisp', compiling to C), CLASP (integration with C++ via LLVM), mocl (commercial whole program compiler to C) and a bunch of others.
Thus the support of and integration into is different in Common Lisp implementations, just like it is different in Scheme implementations. GUILE was designed for C embedding, just like some Common Lisp implementations were designed for that task. Guile was also designated as a language of choice for the GNU project, which adds quite a bit community support - while Common Lisp (or one of its implementation) was not:
'Guile is the GNU Ubiquitous Intelligent Language for Extensions, and the official extension language of the GNU project.'
See for instance: https://wingolog.org/archives/2019/06/26/fibs-lies-and-bench...
Nowadays most Guile users write exclusively Scheme code, resorting to the FFI for code reuse rather than for performance reasons.
Unless I’m misunderstanding, the author sounds like a developer that’s primarily used JS. It’s really not abnormal to have to include parens after a method name in a language, which indicating that you are calling a function.
In common Lisp you can call a regular function defined with defun as (foo "something"), but if bar is a variable containing a fuction you have to use (funcall bar " something"). I agree that this is weird and confusing.
You need the * to access the function the variable points to.
It's more like Ruby's method(:foo) and .call().
That's not what he means. Assuming some pseudocode with a more common syntax to prune the parentheses question, Common lisp is doing this:
function f(x) { print(x) }
let a = f
funcall(a)(27) # prints 27
Whereas Scheme is doing this: function f(x) { print(x) }
let a = f
a(27) # also prints 27
This is the fundamental difference between so-called Lisp-1 and Lisp-2 families, depending on whether functions and variable share the same namespace or if they have to be “lifted” from one to another through funcall.When I ask to have the advantage of a Lisp-1 explained to me, I get some handwaving about "elegance", rather than a specific explanation for why someone would want that. Personally, I find a Lisp-2 (or a Lisp-N) to be more convenient and more understandable. And I don't have to have lexical variables named "lst".
Ruby, PERL, R, Shell languages, Elixir.
The closest parallel I can think of is Scala implicits. You give me a fragment of Scala code using implicits it might be hard to know _what_ is going to happen. Similarly, having a bunch of namespaces (or, perhaps more importantly, leaning into them and having many name overlaps) just means that it's harder to read a single code fragment and know what it's going to do.
Though I'm pretty partial to the argument that, like..... yeah you're going to call a function so the name refers to a function. But without extra symbols around this concept to help the reader, in this age it seems like a concept that is hard to take advantage of.
(here ...
versus (... here ...
That's what makes possible the two spaces.It encourages programming more in a functional style. It's much easier to implement and use higher order functions, if functions are treated like any other object. In Common Lisp, functions defined with DEFUN are not, by default. Common Lisp supports the functional style, but in order to operate on functions with higher-order functions, you must first scoop them out of their special namespace with FUNCTION; and if you want to apply a function you have as a value you must FUNCALL it.
Lisp-1s remove these extra steps, and to some programmers that is more understandable.
As for going in the other direction: I examined the Quicklisp source for how often funcall occurs: on about 0.4% of lines.
I will claim that adding these makes the code more understandable, not less.
So
(mapcar #'f list)
instead of
(mapcar f list)
?
Doesn't ABCL let you do the same?
Probably the main thing that distinguishes Racket is that it's meant to be a playground for experimenting with programming language design.
It's also less consistent than proper Lisps. For example, it's possible to produce a Java OverflowException using Clojure math operators. Some operations will gracefully upgrade to bignums, but others don't.
One thing I really like about this book is it assumes you know basic programming ideas, so it gets into distinctively “lispy” features relatively quickly. The chapters on exception handling and Common Lisp’s object system, in particular, really sort of blew my mind and had a fairly profound influence on the way I think about programming languages.
A bunch of us from Freenode IRC have formed Common Lisp Programming Challenge (CLPC) at https://github.com/spxy/clpc in order to help beginners get started with Common Lisp (CL) while learning the language from Practical Common Lisp by Peter Seibel.
The outdated environment setup in the book can indeed be a genuine hurdle for a beginner. Portacle is a great way to get started. In the CLPC repository though, we are documenting the steps to set up Emacs, SLIME, etc. from scratch, so that a beginner to the CL ecosystem can try out and understand the traditional way of installing and setting up these tools.
If anyone here comes across this comment and is already reading or about to start reading the book Practical Common Lisp, or is eager to learn Common Lisp, you are welcome to visit https://github.com/spxy/clpc and join the Common Lisp Programming Challenge.
Here is what I recommend:
1. Study Scheme for an introduction to programming in a Lisp-like language. I used Structure and Interpretation of Computer Programs as one of my course textbooks (I also assigned my students separate textbooks for the Prolog and Smalltalk portions of the class). My favorite part of this book is its discussion of the metacircular interpreter (i.e., a Scheme interpreter written in Scheme), which is key to understanding how a Lisp works internally.
2. Read the Lisp 1.5 Programmer's Manual. While this Lisp has long been succeeded by more modern Lisps, it is a very nicely designed manual that discusses the implementation of Lisp. It also has a short metacircular interpreter on page 13 that profoundly inspired Alan Kay and many other luminaries of computer science.
3. Begin learning Common Lisp. Common Lisp is much more complex than Scheme is; Common Lisp is to Scheme as C++ is to C. I started getting serious about learning Common Lisp earlier this year, and I am currently working through the Advent of Code exercises in Common Lisp. My favorite intro book is Common Lisp: A Gentle Introduction to Symbolic Computing. I also own a copy of Common Lisp Recipes, which has been valuable for learning Common Lisp idioms; part of the challenge of learning a large language is figuring out what are the most idiomatically correct ways of doing things.
4. Read The Art of the Metaobject Protocol. I have a copy of the book, but I haven't gone through it yet. My goal is to work through this book to have a full understanding of the Common Lisp Object System.
Now, Guix includes some 15000 non-Scheme packages, so it is somewhat "overkill". OTOH you can also install/run/dockerize a world of Common Lisp packages with Guix. :-)