I chose Scheme over Common Lisp
I know this may seem like heresy, but it’s the truth. A few weeks ago I made the choice to move from Common Lisp (I had been using the Allegro implementation for about 2.5 years) to PLT Scheme.
I don’t want to go on and on like I know have reached some level of enlightenment that CL users haven’t, but I would like to briefly list some of my feelings about the switch.
What I like better about Scheme even though I once mentioned it as a reason why I wouldn’t
- Functions and variables in the same namespace
This always seemed like the move from a Lisp-1 to a Lisp-2 was a positive thing. You wouldn’t have to worry about having a variable that clashed with the name of a function, but it seems like was only an issue of having way too many functions with crazy names all over the place. The big problem that it creates is that you have to use FUNCALL to execute a function store in a variable, you have to use #’ to pass a function to an applicative, and you have to treat functions different than variables in general. (If you want to see something really scary try looking at the Y Combinator implemented in Common Lisp)I’ve found that in Scheme my code is more elegant, tends to be far more functional and the only real concession I’ve had to make is that i can’t call a variable LIST (like any one ever actually does that, even in CL people use LST or LIS).
I should also mention that I’ve read that as of R6RS Scheme will be case-sensitive by default, which would allow you to use case conventions to separate variables from functions if you so desired.
- The define syntax
Whenever I used to try and read Scheme code I found myself overwhelmed by the define statement. Not only was it significantly different looking that the defun, defparameter, and let statements that I was used to, but that they were used differently, they were nested all over the place, functions created inside other functions and other odd things that made my brain hurt.As it turns out, for me, using (DEFINE (square x) is way more natural for lisp than (defun square (x), since in the end it’s the same syntax as that way that you call the function. Also by using Swindle (an add-on to PLT-Scheme) you have the syntactic-sugar available for currying.
(define ((add a) b) (+ a b))
winds up being compiled to
(define (add a) (lambda (b) (+ a b)))
Like I said above I find now that my Scheme code is way more functional than my CL code ever was and I think this is a big part of it, I wind up using recursion and folds all the time instead of the doTimes, doList, while and loop constructs that I used to use. As much as this was a bit hard to get used to, it wound up being way more readable and concise than the iteration constructs.
- Continuations
Ever since I heard of continuations I was intrigued, but they aren’t available in CL. All the reading that I did as to why mentioned that the reason for this is that it was essentially a GOTO statement, and that THROW and CATCH were available. Also, the feeling that I got was that unless you’re doing a continuation-based webserver that they are just an interesting toy that’s fun to play with.Turns out that I find it hard to believe that I lived without continuations before (the same feeling that I had when I discovered closures). The AMB and AMB-COLLECT macros wind up being particularly useful for permutations. The following code collects a list of all lists with lengths from 1-4, where each item in the list is 1-4.
(define (make-list n)
(if (positive? n) (cons (amb 1 2 3 4) (make-list (- n 1)))
null))
(amb-collect
(make-list (amb 1 2 3 4)))
this is pretty cool too.
(define (between a b)
(if (<= a b)
(amb a (between (+ a 1) b))
(amb)))
(let ((a (between 2 9))
(b (between 2 9))
(c (between 2 9)))
(amb-assert (= (+ (* a a) (* b b)) (* c c)))
(list a b c))
Continuations and the AMB macro are probably going to need their own post. I should also mention that the understanding of CPS-style coding has forever change the way I code javascript and actionscript.
The only other thing that I going to mention is that I really enjoy APPEND! being the destructive version of APPEND instead of the CL NCONC, there are tons of examples like this. These kinds of decisions made by the Scheme crowd actually make a ton of sense, where as when trying to learn CL was kind of like memorizing history.
The only thing that still bugs me about Scheme is the separation of #f from NIL. I know I shouldn’t have a problem with this since it makes tons of sense, but it still bothers me that when I’m using recursion, instead of (IF LST , I’m forced to write (IF (NOT (NULL? LST)).
That’s all I have for now. Let the angry comments begin.
Comments
Heretic! Aren’t you asking for trouble.
You forgot the lack of CLOS which is a very good thing. It is pleasurable to download a lib written in Scheme on the web and expect it won’t be using that devil of an object system.
I started out with common lisp too, stumbled upon PLT Scheme and have never looked back. DrScheme has the best documentation system I have ever seen, MrEd (the gui library is a pleasure to work with).
hip hip hooray for Scheme.
You can define macro for (if (not (null? …)) … …) with some short name.
I’m using CL, though.
The best thing you can do with Scheme is implement Common Lisp in it
Seriously, I don’t think there is a real choice to be made between CL and Scheme. The only think thats kept me away from Scheme is the lack of a Slime: having picked up CL and Slime first, I find I’m wedded to that environment. Interactively previewing Macroexpansion is *so* convienent.
What are you writing? Are you making this move mid-project? Just declaring your love?
Why not just make a macro like IFNUL?
Only Paul Graham names CL variables “LST”.
Welcome to Scheme!
Just a minor note: mutable pairs are going away from PLT Scheme in the next major release. I’d be curious to your reaction to this blog post explaining how we see the issues:
http://blog.plt-scheme.org/2007/11/getting-rid-of-set-car-and-set-cdr.html
if lst is always a list then you don’t need to say
(if (not (null? lst)) …)
you can say (if (pair? lst) …)
but if lst can be an atom then you need the null check (but then the name lst is unintuitive.)
Also, in response to an earlier comment about macro expansion and steppers, check out Ryan Culpepper’s work:
http://www.ccs.neu.edu/home/ryanc/macro-stepper/
I keep pulling off the petals of the flower one by one, saying,
“I like the simplicity of Scheme,”
“I hate the simplicity of Scheme.”
You give convincing points, but I’ve gotten kinda attached to Slime, for reasons I’m not even sure I can explain, so Scheme comes and goes but never lasts longer than a summer love. The bitch—I mean, CL—always keeps calling me back after I’ve had my fun.
Welcome to DrScheme, version 371 [3m].
Language: Standard (R5RS).
> (let ((list ‘(a b c)))
(length list))
3
>
Thank god you didn’t mention the worst of the scheme heresy, the fact that you don’t have to use a mess of parenthesis.
The fact that ([]) and [()] are functionally equivalent, to (()), in scheme is one of the biggest advantages over lisp that I find, I find my eyes can scan a nest of them, say in a let/let* block, much faster than the lisp equivalent.
If you’d mentioned that, the lisp faithful would have you in the town centre on a bonfire as we speak.
I’ve been feeling a lot of the same pain points with CL as you describe. The Lisp-2 namespace just seems to make doing things with higher-order functions too much hassle for my liking. I’m never sure when to, and when not to, use funcall, apply, #’, and their various variations. Defining functions locally within another function should also be easy like it is in Scheme, rather than the confusing mess of labels or flet or flet* or who-knows-what-else. If you’re having a hard time with naming collisions, that’s what packages/modules are for.
OTOH I’ve become attached to SLIME. DrScheme just really doesn’t do it for me, it has a lot of irritating behaviors like clearing out the REPL window when you run the code in the source window, not having history, having editing features that pretty well amount to Notepad with syntax coloring and paren matching, steadily chewing through memory if you leave it running idle, being slow, and screwing with my Windows mouse-speed settings (I mean what’s up with that? OS-provided mouse drivers not good enough, they had to roll their own?)
[…] dynamic/latent typing that I had come to associate with Lispy-ness. Only recently did I find that I’m not alone in this experience, which helps me realize that it isn’t just a case of me being […]
I also prefer Scheme over Common Lisp because it emphasizes intuition. While Lisp has a thousand more features, changing interpreters or platforms makes for a thousand shackles that need to be ported.
Give a man a definition and he can design for a day. Teach a man how to define and he can design for a lifetime.
Post a comment