Author of this blogpost here. I think the post is good, though it's mostly aimed at the perspective of "Racket is a very practical Lisp, for getting things done, akin to how Python is a very practical lisp" and also "DrRacket gives an accessible entry-point for Lisp for newcomers." Unfortunately I don't think Racket quite took this to be the rallying cry I hoped them to, to embrace Racket's lispiness as "The Python of Lisps" (which was the other alternate title I was debating between when I wrote it).
But in the comments here and as with most articles about Lisp, the conversation moves to... can newcomers understand Lisp's syntax? My spouse and I gave a talk about this very topic: "Lisp but Beautiful; Lisp for Everyone". https://fosdem.org/2022/schedule/event/lispforeveryone/
There's a lot in there (including demonstrations of representations of lisp which don't have parentheses at all). But the biggest point in the talk is that lisp isn't scary for new programmers... it's scary for experienced programmers. As we talk about in the video, my spouse and I co-ran introductions to programming using Racket aimed at humanities students with no required prior programming experience whatsoever.
What we found was that the students who had no prior programming experience, after just a few minutes, had no problem with the syntax (and DrRacket made it easy enough to pick up the language). But we did have complaints in the class about lisp's syntax... from those who already knew how to program. They already had a frame, and within that frame, lisp's syntax looked alien. Without a frame, students just accepted it and did fine with the syntax... great, even. (Many of them praised its clarity; like any decent lisp editor, DrRacket helps identify the kinds of parentheses-oriented errors that users think they're going to encounter.)
Anyway, I do more work in Guile these days than Racket, but both are really great systems with great communities. The thing I miss most about Racket though is that DrRacket gave an easy entry point for students which wasn't "and now start learning Emacs". (Guix is pretty great, however.)
Lisp syntax is pretty rough if you want to prioritize maintainability. I can look at blurry C code and know generally what things are and what’s going on without a single character being legible. Lisps don’t typically have the same convenience. Given that code maintenance is usually about poring through thousands of lines of code quickly, I’d say this is a significant advantage for C.
However, Lisps have unparalleled macro flexibility. It’s a clear trade-off that Lispers often have trouble admitting is a trade-off.
I'm kind of curious how much of this is the problem with parens, and how much of this is the conventions that Lisp programmers seem to mostly adopt. For instance, here's the concise 99-bottles from Rosetta Code for Racket:
(define (sing bottles)
(define (plural n) (~a n " bottle" (if (= n 1) "" "s")))
(printf "~a of beer on the wall\n~a of beer\n~
Take one down, pass it around\n~a of beer on the wall\n\n"
(plural bottles) (plural bottles) (plural (sub1 bottles)))
(unless (= 1 bottles) (sing (sub1 bottles))))
(sing 99)
And even with a snippet this short, it's not quick for me to scan. Lispers generally like hanging parens, the indentation varies depending on what construct you're in, and that example uses recursion instead of a for-loop [0].
Ok, so let's turn the concise Python one into a hypothetical Lisp:
(for i in (range 99 0 -1)
(let b = "bottles of beer")
(let w = "{b} on the wall")
(print "{i} {w}, {i} {b}\n")
(print "Take one down and pass it around, {(- i 1)} {w}.\n")
)
I think you could "know generally what things are" with that version even if it was blurry. And of course, this "hypothetical lisp" is legal Racket/Scheme after implementing a few macros.
[0] I know Racket has for loops, but they didn't choose to use them in that example. Which is my point.
1> (defun bottles-verse (n)
(flet ((bottles (:match)
((0) "No bottles")
((1) "1 bottle")
((@x) `@x bottles`)))
(put-line `@(bottles n) of beer on the wall\n\
@(bottles n) of beer\n\
Take one down; pass it around\n\
@(bottles (pred n)) of beer on the wall\n`)))
bottles-verse
2> [mapdo bottles-verse 5..1]
4 bottles of beer on the wall
4 bottles of beer
Take one down; pass it around
3 bottles of beer on the wall
3 bottles of beer on the wall
3 bottles of beer
Take one down; pass it around
2 bottles of beer on the wall
2 bottles of beer on the wall
2 bottles of beer
Take one down; pass it around
1 bottle of beer on the wall
1 bottle of beer on the wall
1 bottle of beer
Take one down; pass it around
No bottles of beer on the wall
nil
3>
Why does 5..1 go from 4 to 1? Because 1..5 goes from 1 to 4; it's a half-open interval.
5..1 goes over the same values in reverse.
:match is a "parameter list macro", which is something I invented. flet knows nothing about pattern matching; it transforms to a lambda, which also knows nothing about pattern matching, but :match takes the parameter list and body, transforming the code using the pattern expander logic.
String literals and quasiliterals can go across multiple lines, but with the permission granted by a continuing backslash. Being explicit about this lets us catch errors when a literal is left open accidentally. All unescaped whitespace before a continuing backslash, and leading whitespace at the start of
the next line is deleted:
"abcd \
efgh" -> "abcdefgh"
"abcd \
\ efgh" -> "abcd efgh" ;; escaped space counts
"abcd\ \
efgh" -> "abcd efgh" ;; on either side of \
I think I must be really bad at communicating, because you aren't responding to any point I was trying to make. Let me try again :-)
I think Racket, Scheme, and some Lisps are conceptually elegant, but they don't read well to people like me (or apparently the poster above).
In particular, your snippet of code has 3 different indentation strategies, and you hang your closing parens the way almost all Lisp programmers do. To me, it all looks so organic and arbitrary.
So, for those of us who don't like to read other people's Lisp - is it because of the parens? Or is it because the conventional Lisp style is alienating to us?
I really think it's the style. I'd rather read your example like:
(defun bottles-verse (n)
(flet
((bottles (:match)
((0) "No bottles")
((1) "1 bottle")
((@x) `@x bottles`)
))
(put-line
`@(bottles n) of beer on the wall\n\
@(bottles n) of beer\n\
Take one down; pass it around\n\
@(bottles (pred n)) of beer on the wall\n`
)
)
)
That's still not quite as nice as I'd like, but maybe you can see where I'm coming from.
I think most Lispers would say, "You get used to it." But, well we don't get used to it because we just stay with whatever our curly braced or indentation language is instead.
It's more like an experienced user of a bicycle will not use stabilizer wheels. For any person who can ride a bicycle they look weird, unneccessary and actually hindering them. After a short learning phase few people will need them any more and will be happy to get rid of them.
Lisp has quasi-standard indentation styles. They make code more readable and writable. The differences between Lisp and most other programming languages: Lisp code is written/read/manipulated as a data structure.
What you wrote has also different indentation styles, but dangling parentheses are seen as not helpful: Lisp users don't count parentheses, they don't match parentheses like that, their code is indentation heavy, code can get very long and code can be generated. Lisp macros are an example of generated code. That means Lisp users also might look at generated code, for example when they check the code a macro generates or when one users a Lisp interpreter during debugging. The Lisp interpreter directly executes Lisp source data structures.
Lisp users read by indentation of lists.
The parentheses in Lisp have a different purpose as curly braces in other languages. In Lisp the parentheses are used to denote a data structure: lists. The syntax for lists (or more correct s-expressions) has its own syntax. Programs are written as nested lists of other objects. Thus the programming language Lisp is defined on top of lists. It's possible and not unusual that code is not written to a file, but generated in memory or by typing to a Lisp read-eval-print-loop, where some will directly treat the input as data, not as text.
We we want to write lists as compact as possible and use indentation to make groupings visible, where necessary.
The Lisp programmer learns to write/read/manipulate lists as a base. Programs are then written as lists.
You aren't telling me anything I don't already know.
> Lisp [...] indentation styles. They make code more readable and writable.
This is a matter of opinion, and you're free to ignore the majority or programmers who don't like Lisp. Trust me though: there are a lot of smart programmers who reject Lisp, and it's not because they "just haven't learned to ride a bike" yet.
My only question is whether the people who don't like Lisp dislike it because of parens (which is the conventional wisdom) or because of the arbitrary indentation and closing brace style.
> Trust me though: there are a lot of smart programmers who reject Lisp, and it's not because they "just haven't learned to ride a bike" yet.
You aren't telling me anything I don't already know - to quote your remark from above.
The questions of code formatting and Lisp syntax has been discussed since the early 1960s. I've taking part in such discussions myself since the mid 80s.
My take: people don't like Lisp or reject Lisp because of MANY different factors - and many are justified, based on what they are looking for - not everybody needs to like or use Lisp - if there are better programming languages with their eco-systems for many programmers, that's totally fine for me.
Syntax is a factor why people don't program in Lisp. The standard formatting rules are a minor factor. More important is that the syntax&semantics is difficult: the consequences of code as data is confusing them - for example they have to deal with quoting/evaluation rules, they see that there are macro expansion phases which make code understanding and debugging harder, etc.
> ... because of the arbitrary indentation and closing brace style.
Lisp does not have an 'arbitrary' indentation and closing brace style. There are clear rules and these are supported by tools in various ways.
'being older is not a virtue in itself' (using one of your phrases), more interesting would be the experience of actually seeing people and helping them trying to learn Lisp in different settings. For example: at the local university we had several hundred students each year who had to learn Lisp and/or Scheme (and other programming language) as part of their extensive computer science introductory courses - most students had no prior knowledge of Lisp. That was at a time when we used printer terminals or vt terminals to edit code. Later one could see students who could do programming homework on their personal computers or with UNIX workstations at the University...
A few years back, both my brother and father wanted to learn to program. Actually, relearn in the case of my dad. He was doing assembly programming for fun when I was a kid, and he taught me Basic. So really, he wanted to re-learn after a 30 or more year hiatus.
I tried to turn them both on to DrScheme (PLT wasn't named Racket yet). I figured the language is elegant, the tool is friendly, it's got libraries for tons of things, and the syntax is really simple. Kind of the "learn the language in the first part of the first chapter" approach in SICP, and then get to doing something fun. It's a much better language, environment, library, and implementation than Python. But it really didn't pan out.
I'm sure we can blame me for being a poor teacher, but my brother got turned off completely, and my father seemed pretty annoyed it wasn't anything like he remembered. After that, I showed him Python, and he ran with it for a few months before he switched back to doing his pet projects in Excel. He's older than both of us, so he can do whatever he wants :-)
Anyways, I kind of like Scheme, and I've been installing various implementations of it on my computers for the last two decades. Of course I must not like it that much, because I almost never use it for my pet projects. Maybe that's because I like numerical stuff and am too lazy to sort out an infix macro that makes me happy. It's just easier to use Python or C++ (or lately, Rust).
Elsewhere, you made the comment "The Lisp version looks similar to the Python code, plus parentheses". Apparently it wasn't obvious, but that was intentional :-) I just wonder why people (like my father) find Python so accessible, when you can make Scheme look nearly identical if you ignore the parens. In every way that counts, Scheme is at least as expressive and has much better implementations than Python... Yet people prefer Python.
If I ever make my own compiler, I'm inclined to use parens as delimiters for expressions. I'll spare you the details, but it won't really be a Lisp, and it would be more block structured. I only bring this up because in your first reply above where you were proselytizing and pontificating, you were already preaching to the choir. Since that OP way up there made a comment about it not scanning well (looking at it with blurry text), I wanted to hear from someone who doesn't like Lisp what it would take to make it likable.
And of course all my comment did was draw attention from two people that want to defend Lisp. :-)
But, it turns out, it basically isn't. In the Lisp world there is a tremendous consensus about it; nearly everyone does it in nearly an identical way to everyone else. This is in stark contrast to the way, say, C or C++ people lock horns over formatting.
Even if you might not like the particular way it is done, the dogged consistency has to count for something.
> arbitrary indentation and closing brace style.
Lispm demonstrated how this style is reproduced by a Lisp system's printer. It's codified into a documented algorithm, and one that isn't hugely complicated.
> But, it turns out, it basically isn't. In the Lisp world there is a tremendous consensus about it; nearly everyone does it in nearly an identical way to everyone else.
Yes, and that's self selecting. If someone hates that style, that might be one reason they don't stick around in the Lisp world.
Besides, something being a consensus has little to say about whether it's an opinion or not.
> Even if you might not like the particular way it is done, the dogged consistency has to count for something.
Lol, no! Just like consensus, consistency isn't a virtue on its own. You could be consistently doing it wrong. :-)
No, people could just use their style. We've seen that in some subgroups. Often for example with AutoCAD users scripting in AutoLisp / VisualLisp.
> consistency isn't a virtue on its own. You could be consistently doing it wrong. :-)
Consistency is a virtue on its own. Consistent coding standards improve readability for a wider group, even if the coding standard would be slightly less optimal according to some other metrics.
Helping people cooperate is a virtue. You're confusing the means with the ends, and it's very likely that the OP I first responded to above does not find the Lisp-consistent style very readable, so it didn't enable his cooperation.
They made a point of saying, "I can look at blurry C code and know generally what things are and what’s going on without a single character being legible." That sure seems like their problem with Lisp is layout/formatting.
"I can look at blurry C code and know generally what things are and what’s going on without a single character being legible. Lisps don’t typically have the same convenience"
> in particular, your snippet of code has 3 different indentation strategies, and you hang your closing parens the way almost all Lisp programmers do. To me, it all looks so organic and arbitrary.
In Lisp we don't align Lisp code according to only the parentheses.
A Lisp programmer looks at the symbol in front and based on that there are one or more strategies for indentation. On my Lisp Machine I can cycle through different indentations or I can completely layout the code (called 'grinding' in old Lisp).
There are different lists:
a definition macro
(defun name (arg1
arg2)
a
b)
a function call
(foo bar baz)
(foo bar
baz)
(foo
bar
baz)
a special form:
(if a
b
c)
(if a
b
c)
(when a
b
c)
Property lists:
(:name foo
:age 23)
and lots more. Each macro has a simple implementation style but typically an editor may have custom layout rules, depending on the macro and its syntax.
I write Lisp mostly in the same I write C, and the way I wrote C before learning Lisp. (Except for not closing braces on the same line; though I experimented with that style and it was fine.)
printf("Hello, %s\n",
get_user_name());
In C, it is common to close parentheses together though. Look at the alignment in the tm_diff function in glibc:
371 int days = (365 * years + intervening_leap_days
372 + (a->tm_yday - b->tm_yday));
373 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
374 + (a->tm_min - b->tm_min))
375 + (a->tm_sec - b->tm_sec));
Vim does this alignment; I'm confident I could visually select these lines, hit = and they would stay the same.
The author put extra parentheses around the initializer for days, and around the return expression. I almost certainly know the reason; doing so encourages the editor to do the alignment of the + operators with the interior of the parenthesis.
Vertical alignment is important because if you violate it, it leads the eyes astray:
`@(bottles)
@(bottles)`
Both expressions are inside the quasiliteral: they are parallel elements. Why has one de-indented, as if the literal had already closed in the previous line?
There could be another argument after the quasiliteral:
(put-line `@(bottles n) of beer on the wall\n\
@(bottles n) of beer\n\
Take one down; pass it around\n\
@(bottles (pred n)) of beer on the wall\n`
output-stream)
If you have it like this:
(put-line `@(bottles n) of beer on the wall\n\
@(bottles n) of beer\n\
Take one down; pass it around\n\
@(bottles (pred n)) of beer on the wall\n`
output-stream)))
you're pepetrating a trompe l'oeil. The transition out of the literal is masked, making it look like output-stream is still part of the literal.
I see you like four space indents, and I've used them in C in years past.
Even if I wanted to experiment with four space indents in Lisp, the editor I use wouldn't support it. Believe it or not, the two space indentation is a hard-coded behavior in Vim's Lisp indent mode.
Vim doesn't support the quasiliteral formatting I'm doing; I had to make the one space adjustment by hand.
In short, there is almost nothing arbitrary about the way I'm formatting anything.
Without a doubt, C programmers have different styles. And there's disagreement about what to do when an expression gets too long.
However, almost no C programmer would stack and hang their closing curly braces the way Lisp programmers do with closing parens. And for the few exceptions you'll find, they're almost certainly Lisp programmers making a point about not doing what the Romans do when they're in Rome.
> In short, there is almost nothing arbitrary about the way I'm formatting anything.
Heh, I didn't mean it as an attack on you. I meant it how the rest of us perceive it. Forgive me :-)
I guess it's only natural that you should defend your own style. I'm just curious if maybe part of the reason Lisp is so objectionable to most programmers is because that style is so different from block structured languages.
Many people here seem to like Python. So they think this is fine:
def doit(n):
for i in range(n):
print(i)
Many fewer people seem to like Lisp. So maybe this is less likeable for some reason:
(def (doit n)
(for i in (range n)
(print i)))
Ok, so is this more likeable?
(def (doit n)
(for i in (range n)
(print i)
)
)
Just curious.
> Even if I wanted to experiment with four space indents in Lisp, the editor I use wouldn't support it.
That's pretty circular though... Lisp programmers expect a certain indentation, so editors provide it.
The Lisp version looks similar to the Python code, plus parentheses. As a Lisp user I would read the Lisp code just like the Python code, by largely ignoring the parentheses, looking at the indentation and blocks of code. As a Lisp user working with code manipulation I then profit from that Lisp can be pretty printed by a tool, since the indentation is not The dangling parentheses then don't add any value. Actually they waste space and are getting in the way when typing to a read-eval-print-loop (REPL).
I haven't seen anyone typing to a REPL dangling parentheses.
CL-USER 23 > (defun foo (a)
(list
(list
a b c
)
)
)
FOO
> I guess it's only natural that you should defend your own style.
I have reasons for writing things that way, that can be documented, if necessary. The style is not my own, though.
> However, almost no C programmer would stack and hang their closing curly braces the way Lisp programmers do with closing parens.
But they do it all the time with parentheses, which is the normal thing to do.
By the way: spotted lots of this in the Zstandard compression code base a few days ago:
void fun()
{
if (...)
{
for (...)
{
...
} } }
Yikes; the closes braces line up with the opening ones, but not their corresponding matching ones. I hope that wasn't a Lisp coder (and don't suspect it).
> I'm just curious if maybe part of the reason Lisp is so objectionable to most programmers is because that style is so different from block structured languages.
But it isn't. Almost all the indentation and alignment strategies you see in canonically formatted Lisp code have counterparts in everyday code in mainstream languages.
Found this in Linux's kernel/sched/core.c:
WARN_ON_ONCE(debug_locks && !(lockdep_is_held(&p->pi_lock) ||
lockdep_is_held(__rq_lockp(task_rq(p)))));
^ Lisp disease?
^ gee what is this "arbitrary" alignment?
Would C be more popular if it was more typically like this?
All of these examples use pretty much identical formatting logic as what I'm following when I'm coding in Lisp; except that a more pure version of it occurs.
This is a predominant style used in writing "curly-braced C-likes"; you see it everywhere and examples can be found readily. You simply don't seem familiar with this subject area.
Heh. I see we've gotten to the point where you switch from arguing the point to questioning my qualifications. Gross.
I guess that means I should return the insult: Showing a few examples of ugly formatting doesn't prove that curly-braced languages are that way "everywhere". And besides, other than the one silly example from Zstandard, which many people would agree is unusual, you didn't even show examples of curly braces. Even with all the different styles of C people use, the block structure is pretty consistent. You're either being deceptive or you need work on your logic skills.
Go ahead and grab the last word if you want it, but I think we've gotten away from anything good in this discussion.
I read your article when it was first published and agree with the sentiment.
Off topic (sorry) but one thing Racket is missing is great Python interop to mix in deep learning and other libraries. Common Lisp and Clojure have this support. Years ago I tried saving trained Keras weights and using them in Racket with the Matrix support: it works, but not very fun to use.
Couldn't this go the other way too? People who have no experience with programming languages have no basis for evaluating the comprehensibility of a language's syntax. I didn't have any complaints about BASIC until I tried Pascal, etc.
Grinnell alum here - our first CS class is in Scheme. I expected the class would be a breeze because I already had a few years of programming experience in Java/Python in highschool. Turns out that lisp is way different, and I was about as challenged as people with 0 programming experience. At first most of us were annoyed at having to learn an esoteric language without real 'industry' use, but by end of semester we all were super into it. It helped that our curriculum focused on fun problems like using recursion to draw things using a faculty/student developed scriptable gimp library.
I remember a talk from a Michigan State professor at one Pycon a ways back that taught Python for their intro to CS. What I remember is that for the first time ever, he had people call him after the class and wanted to switch majors.
I think Scheme is awesome as long as it's not your first language, but I think if Scheme is your first introduction to CS particularly in college, it can be pretty brutal depending upon the instructor. I think a lot of higher learning institutions were trying to copy MIT's use of SICP for their intro courses.
We used Racket in the early 2000's at university, in the first year of CS. Everyone (including me) was appalled by it (we already knew C, Pascal, Java, etc.)
After a few weeks it "clicked", and since then I've been a huge fan of Scheme. When other students went out of their way to write C-to-Scheme converters to keep coding their homework in C, if you fully embraced the language it felt something close to enlightenment ;-)
I used Scheme later to teach programming to absolute non-science people and they all simply "got" how it worked, even though they had previously struggled doing even very basic things in Excel Macros and the like.
Scheme is a perfect tool for learning how to program, and it is even useful later to write complex programs in production. Yes, C might be faster, and Python might have more hits on StackOverflow, but to really understand the concepts behind polymorphism, recursion, lazy evaluation, oop, etc. Scheme helps a lot with it's simple syntax.
Racket was also my first exposure to lisp and I recoiled from it as well. Shortly after I ended up working in clojure for a while, which I felt "fixed" a lot of the things I thought I didn't like about racket.
I still think the clojure syntax and immutability semantics are prettier than racket's but that's just my own vanity. I'm back on racket for the reasons mentioned in the article pretty much: good fundamental language, big and practical standard library, plus every freak academic CS thing you could ever want to play with available as well.
The tooling and community also encourage you to think of problems in terms of DSLs, something it partially shares with ruby as well. That approach isn't always a good fit, or one you can get buy-in for, but when it works it works well for me.
I sometimes like to imagine a world in which .NET was cross-platform and open source from the start such that when F# was introduced it took over everywhere Python was popping up. It's a fantastic world to imagine but a depressing reoccurring dream.
I love F#, but honestly Microsoft really hadn't built up the kind of trust back then (and maybe even now) to make this an even remotely realistic scenario.
Trust is one issue, another mah be perception. I know Microsoft has been embracing and extending open source recently, but I didn't know F# was open source. Even then, I still have the impression it's very much "Microsoft stack" and not worth investing energy in unless I'm doing "Microsoft stack" stuff.
Depressing is something of an understatement. It's a fundamental unfairness of this world. We have all these nice technologies that never reach critical mass while far worse projects enjoy the support and investment of the whole world. Such is life for all Right Things.
Unfortunately for your timeline Python was already in wide use before F# was released (2005). You'd need .NET and F# to have been invented a little earlier for that to work out.
Yes, but it was before the Python 3 debacle, and certainly early enough to be a contender. F# is a vastly superior language compared to Python and the core is much simpler. And look at Visual Studio Code for an example when Microsoft puts their might behind something. It only took 3-5 years for Visual Studio Code to dominate. But obviously this is all hypothetical, because it also needs Microsoft to treat F# as something other than a research tool to bring features into C#. I really don’t know why they didn’t view F# as a way to take market share from Python. It could have brought legions into their ecosystem. (Hypothetical given many’s weird obsession with hating anything Microsoft does.)
Hey I was using ocaml before F# was a thing so you don't have to sell me on the advantages of F# as a language. In fact F# seems to solve the one thing that I used to find really annoying about the ocaml ecosystem which was building with different libraries and extensions (it's been a while but I remember lots of painful headscratching trying to figure out problems with this).
The anti-Microsoft bias at the time was pretty strong in most of the open source community given the various dirty tricks that had been employed against Linux around 2000. Within the corporate world there was a big strategic split between folks who were all-in on microsoft and .NET vs people who wanted open source and Java (I worked at a big bank at the time and this was a very sharp point of division with careers made and broken on the back of tech choices, fear or otherwise of vendor lock-in etc).
As for MS's strategy, my recollection is at the time MS was all-in on C# and trying to replace C++ and Java rather than pushing F#, which was somewhat of a side-gig for them. Visual Studio was very dominant in terms of IDE then both for C++ and C#, with only Java guys and unix nerds like me using anything else.
My ex who wasn't from stem background was learning Python. Its very anecdotal, but few things I very well remember was:
(1) Here getting confused with scopes. E.g. assigning variable in one function and using it somewhere else, mixing global/local scope, assign/use order. Looking at that I was thinking a language that has more explicit and strict way to declare variables and look at scopes is probably better for beginners. The alternative with Python is that it can be somewhat confusing, and at times you see them only if they get executed. Racket definitely doesn't have that problem. It is as obvious as it can be.
(2) If your introduction is through some kind of project (data science, web app ...) you'll likely be using some object oriented features from very beginning. I saw her getting confused with `foo()` vs `foo.bar()` vs `foo.bar`. There was definitely a big gap in her understanding, but I think Racket's focus on functional paradigm and most libraries being that way -- is somewhat a nicer experience for beginners.
Overall, Racket is a simple language but can get fancy as far you'd want it to, so I think the barrier of entry is probably better than Python.
The problem is that Python doesn't make the explicit distinction between declaring a new (shadowed) variable, and assigning to an existing variable from an outer scope
> I saw her getting confused with `foo()` vs `foo.bar()` vs `foo.bar`
I had the same problem when learning Python. Top that up with mutable and non-mutable structures and it can get very hard to make sense of Python as a beginner.
I remember I had a theory that foo.bar() would change foo and bar(foo) would return a value while leaving foo unchanged ... that turned out to be very wrong.
It also took me forever to realize that when you call foo.bar(), foo is the first argument of bar().
Hum... Python have a simple advantage: easy to learn, being mostly imperative, especially for those who have a background (even very little) in such languages (most students) and it's quick to fire up and also present almost everywhere with pip and venv to help.
Racket is a far more powerful tool, but also less immediate to learn, especially for those who have a background with imperative languages, not fast at all to fire up and not easy to use as a glue for anything else.
Python is a kind of bridge from unix shell scripts not so many nowadays have practice with and a script-versed programming language. Racket is about programming in classical terms, with an unique ecosystem.
In that sense probably Smalltalk with Pharo or classic (old) Lisp with Emacs are more acceptable Python since they do not act as a glue but they are FAR more immediate to use and have a purpose on it's own behind programming: they are end-users programming environments, as Python is an end-user scripting tool.
> I dreamed in parentheses. I'm not kidding, the only dreams I've ever had in code were in lisp
Every time I dreamed of code, I could never actually get what language I was writing in.
But, each time, the code text was wrapped thin and was long.
The theme was just perfect. I have looked at a lot of themes and color-schemes for vim (and terminal) and VS Code, but could not match the one from the dream.
It was just perfect.
And although I write a lot of Python, I think the code I dreamt of was typed.
> We found the age-old belief that "lisp syntax is just too hard" is simply false; the main thing that most people lack is decent lisp-friendly tooling with a low barrier to entry, and DrRacket provides that.
If the syntax requires special tooling to make it usable, isn't that a sign that the syntax is hard?
Are there many people who believe Lisp syntax is as easy as Python or other non-Lisp languages? If that's true for you, I'd be interested in hearing more about your experience.
I find it's better to think in terms of "familiar" vs "not familiar" rather than "easy" / "hard". Lisp syntax to me is the easiest -maybe with Forth and Smalltalk- syntax around, certainly orders of magnitude easier than Python's. And I don't mean just semantics, but also conceptual beauty. There's something about it that feels so elegant that other languages do not possess (again with Forth / Smalltalk exceptions). I can take a young curious kid with no previous exposure to programming, and teach Lisp syntax in under 5 minutes.
But a kid with no previous exposure to programming is nothing like your average developer with strong pre-existing conditioning about how programming languages must look like. And this is the issue I feel with a lot of folks complaining about Lisp syntax. It's easy but it's not _familiar_ and some find it harder than others to get used to this discrepancy.
After writing Clojure for a few years, there were several times where I ended up with a complex function with unbalanced parens, and the only way I could fix it was to basically re-write the function piece by piece. This is something I've never had to do in any other language.
I'd also end up in not great places when utilizing parinfer while editing code written using different formatting than what parinfer expected. I'd have to fall back to paredit and hope for the best.
This is all to say that yes, sometimes editing lisp syntax is "hard", at least for me. Turns out some humans make so-so AST parsers.
That said, structural editing is great once you get good at it, and something I miss back in other languages.
> After writing Clojure for a few years, there were several times where I ended up with a complex function with unbalanced parens, and the only way I could fix it was to basically re-write the function piece by piece.
That's the punishment for removing some of the "reduntant" parentheses from Scheme.
Compare
Clojure: (let [a 1
b 2]
(+ a b))
Scheme: (let ([a 1]
[b 2])
(+ 1 2))
If for some reason, say, `b` get deleted a Scheme compiler can tell you which binding pair has a problem.
In more complicated setups, in Scheme/Racket I indent the piece of code,
and from the indentation I can see, where end parenthesis is missing.
> After writing Clojure for a few years, there were several times where I ended up with a complex function with unbalanced parens, and the only way I could fix it was to basically re-write the function piece by piece.
Mess up Python indentation and you get something like that, but worse.
I love lisp but there is the practical problem of paren juggling. You either need to come up with and execute by hand a line-break-and-indent system on the spot, or use tooling to manage them.
It's not a huge hurdle and other languages have equivalent problems. But the lisp one is particularly ill-suited to other tools, even visual techniques like manually aligning indents.
I've taught programming to beginners, including lisps, and it's definitely one of the early disorientations even without prior expectations about code. You don't need much: I eventually settled on just giving them sublime with rainbow parens and telling them to hit enter a lot. But you need something and it's not obvious what's missing unless someone points you in the right direction.
If Hal Abelson and Jerry Sussman can use Lisp syntax to teach an iconoclastic, supremely philosophical class about programming that must rank amongst the best ever conceived and taught, 40 years ago, with blackboards and pieces of chalk, your average newcomer to Lisp should breeze through the process today.
I've met a lot of folks that seem to possess mental models that are fundamentally mismatched with what Lisp expects: It's not just the syntax but a lot of the base models of the language that demand an open mind and time involvement before they get imprinted. And at least in some cases, blaming the parens becomes the easy way out of why one doesn't get it.
What matters on the blackboard (or a book) is "understanding" and that is not affected by a missing paren or two (which is a testament to Lisp's regularity of syntax).
When they wanted their programs executed by a computer, their editor -Edwin- matched parens, exactly like how one would do it today, or even easier with things like Paredit.
It's not the parentheses or the Polish notation that matter. Those are covered in the first chapter of any Lisp/Scheme book and they are trivial, especially with a proper editor that understands structure and will indent unambiguously accordingly. Unless you're stuck on simple editors like Notepad or Nano, there's no issue here.
What's more challenging in Scheme, as Abelson and Sussman teach it, is the use of recursion instead of looping and in particular understanding that recursion is only equivalent to looping when the recursion is in "tail position", i.e. when there is nothing left for the calling function to do except return to its own caller.
Common Lisp style tends to use looping, not recursion.
I strongly disagree that Lisp syntax is straightforward. Racket/Scheme is decent, but Common Lisp and other Lisp-2's can be incredibly confusing. Having different value and function namespaces for symbols is complicated enough, but the worse problem is that the simple evaluation rules don't always apply.
The base case is simple enough, the symbol-function value of the first symbol in a list is looked up, the rest of the arguments are evaluated or self-evaluated, then passed in to the function. The problem is that when the first symbol is a macro, you need to remember which of the following arguments are evaluated, or taken as unevaluated forms. Anyone who thinks this is easier to parse because there's less syntax either has too little experience with Common Lisp, or is kidding themselves.
The syntax is easy in Lisp, but you don't know what it means without having the vocabulary of operators. The vocabulary is fluid and varies from dialect to dialect.
However, the vocabulary part of a Lisp dialect consists of things which are named. Names can easily be looked up in documentation.
Furthermore, you know what is and is not part of the form even if you don't recognize the operator: you know that (fnoozle blub (grogg)) q42 is invoking fnoozle with two argument expressions, and q42 has no part of it. You don't know how/if those two arguments are evaluated but if you look up fnoozle in the manual, it should be clear. The manual will talk about the arguments, and you can identify which of those is blub and which is (grogg).
The syntax is straightforward. You're referring to semantics. And in that respect, yes, Common Lisp is a very complicated language. The specification is about a thousand pages after all. I wouldn't say it's more complicated than C++ though, and personally I like simple syntax plus complicated semantics considerably more than complicated syntax and semantics. Personally I don't find separate function and value namespaces particularly confusing, but I do agree that it introduces some inelegance. Then again I'm demented enough that I think Perl is beautiful so YMMV.
Even so there are some syntactic hints in terms of symbol naming, like how you can be pretty confident that a form that starts with WITH- is probably some kind of UNWIND-PROTECT macro that grabs a resource and cleans up after itself. Another example is that a symbol with asterisks for the first and last characters names a special variable.
I had little experience with Lisp until recently when I converted a few hundred lines of Lua to Fennel. I have a lot of experience with Python.
I've always been scared of Lisp syntax, but after just a little experience with it it all kind of fades away and it feels like an indentation-based language, while remaining very compact. Now I'm like "why aren't all higher-level languages lisps"?
Editor tooling definitely helps, but it's one of those "skill floor skill ceiling" ideas. Parenthesis-based syntax has a slightly higher "skill floor" (takes more effort to edit), but with editor tooling has a much higher "skill ceiling", where experts can move blocks at the level of syntax rather than characters. Even just vscode's new bracket highlighting and guides are a small feature but a big help to see structure.
No one can convince me that Python is an easy or simple language.
For parentheses, I find Racket and in particular Scheme to be enjoyable. My primary reasons are that it creates a massive amount of regularity and consistency in the language, of which there is none in Python. I had my girlfriend at the time take the How to Code: Simple Data course on edX, which uses the Racket learning languages. Not once did I need to answer a syntax question. Not one complaint about parentheses. She had no prior experience programming.
I find the people who whine and moan about parentheses are usually ones who have massive preconceived notions of what programming should look like or are so deep in some other language they are resistant to change.
I honestly feel like the best textual syntax is something like ML (as found in F#) or Scheme. Anything else is basically just random tweaks and way too much time has been wasted on textual syntax, which is conceptually bounded in terms of its expressiveness. But programmers and computer scientists are extremely myopic. Somehow we graduated from punch cards to text files and have plateaued ever since.
> decent lisp-friendly tooling with a low barrier to entry, and DrRacket provides that
I'm not really sure what the commenter meant by that, but in my experience, all I ever needed was the auto-formatter and parentheses matching DrRacket provides. Matching delimiters occurs in any language, it's just that you have more of them.
> If the syntax requires special tooling to make it usable, isn't that a sign that the syntax is hard?
So, do you write your code on Notepad?
It's interesting that every time Lisp syntax comes up there's a comment like this. But when someone adds "refactoring" and "intellisense" to other languages it gets applauded. Why?
You dont need special tools. But being able to manipulate the sexpressions directly gives you more power.
If we were discussing teaching apprentice roof installers, you wouldn't argue that collated nails (the kind that feed into a nail gun) are worse for teaching because they are harder to use without additional tooling (a nail gun). The 'additional tooling' is in fact industry standard tooling that the apprentice will be using from the very start anyways, and that they need to be comfortable with as well.
Having seen >1mil EUR of machinery stop because of a quick fix to python code done without special editor and thus resulting in having one #\Space less in one line, I would say Python is the language that requires special tools - not Lisp.
Worst case, you can align your parens vertically, like many AutoLISP programmers did when their only tool was Notepad or EDIT.COM. And usually incorrect parens result in code blowing up at build time, not runtime. Counting parentheses is definitely easier than counting whitespace.
If anything, Lisp just happens to be language with community that embraced quite advanced editor enhancements before many other languages got invented, and thus lisper's baseline expectations might be bigger than other developers'
I’ve been persuaded by HTDP that lisp, or at least “beginning student language” (BSL) is probably better than python for non programmers - if the goal is to understand programming.
I believe lisp is easier than python in usage, but ONLY after you learned it. That might sound a bit strange but it's a classic issue most people ignore: ecosystem.
Python is "battery included" in the sense that with the language it's users can access a rich set of ready-usable code pip-able in a venv without any special gimmick and python itself is probably preinstalled in most desktops people who ever write a line of code use, that means python is not a stranger, it's a citizen of a familiar and ready made ecosystem for most users.
Lisp on contrary have many flavors, syntax is similar, but here is (defn, here (defun, ... and CL is still complex and not such immediate. QuickLisp is not here by default etc. Lisp is a stranger in most user's familiar ecosystem.
Non only Lisp is from a civilized age where computer were desktops, a unique ecosystem at the users fingertips, centered on users needs and desire. In that sense lisp is an ecosystem, not a part of a larger ecosystem the user already know. Again a stranger.
Once you know a bit let's say SBCL or Emacs it's far easier implementing something that do not demand something ready-made and absent (the aforementioned battery included) BUT if nobody teach them and only very few know them...
I say that fly a small plane is easier than drive a car, but while I do both things most people do not and so for them I say something alien and the subject of the topic is also alien to them. Most think that's easier turning the steering wheel obtaining a definite result instead of gently hit a stick to see loose effects. Of course that's true, if you are bound to a little path named a road, more specifically a small part of it, however it's far easier NOT being bound to such small areas and loosely move in the air, with far less strict limits all around. You have to try to understand though and so someone have to tech you up front... That's the modern Lisp issue...
> Of course that's true, if you are bound to a little path named a road, more specifically a small part of it, however it's far easier NOT being bound to such small areas and loosely move in the air, with far less strict limits all around.
That's a very interesting observation. It's also interesting in that when you get back to "high traffic" areas where you have to interact with others, the limits get stricter again.
> If the syntax requires special tooling to make it usable, isn't that a sign that the syntax is hard?
I think it is easier to put a nail through a 2x4 than a push-pin, but that presupposes I have a hammer; if I have a nailgun it's going to be easier to put 100 nails in than 100 push-pins. If I have neither, then the push-pin wins. There are always tradeoffs, but it doesn't mean that nails are inherently "hard" and push-pins are inherently "easy"
> Are there many people who believe Lisp syntax is as easy as Python?
Even with python I find myself getting bogged down by missing a parenthesis here and there around function calls, but perhaps that problem is unique to me and my attention and memory issues. Syntax highlighting in editors is great, and certainly it helps me, but I wish for them to be even more interactive and assertive: it should mark fields with obvious errors like unclosed quotes and parentheses, I shouldn't have to wait after compile or run-time to be informed of these errors.
Aid that reduce syntax errors should always be welcome. Moreover, some of us have high working memory and some don't so I welcome opinionated tools like Spyder IDE and Jupyter that are hands-on in the dev stage, and serve to encourage a fun and exploratory dev environment.
Once I got into the habit of typing the corresponding closing entity (quote, paren, backtick, whatever) immediately after typing the opening one, and _then_ coming back to fill in the data in the enclosure, most of my syntax errors disappeared.
It's surprising how many professional devs don't have this habit. I learned it pairing one day.
For me, lisp was pretty intuitive when I first picked it up. It wasn't the first language I learned though.
When I was little my dad had a reverse polish notation calculator he explained and taught me to use. I think it was sort of a novelty for him, but when I encountered lisp in college my first thought was something like "oh this is just like reverse polish calculators".
There was sort of a strange learning curve for me though. The basics were fairly intuitive to me, then things got frustrating, and then easier again.
I really miss lisp though and wish it (or scheme or racket) had more widespread use, especially in numerical computing. I admit, though, that the hardware-lisp gap is bigger in my mind than the hardware-c gap, which extends to more procedural languages.
The TXR language was developed using nothing but Vim, for all the C and Lisp code.
The tooling is the same as for C: indentation, coloring, parenthesis matching, and that's more than you need.
E.g. visually select some code with V and cursor movement, then hit =, and it's nicely reindented.
Vim's Lisp indentation is based on the classic Vi ':set lisp' mode which was in Unix since 1980 something. That is combined with some generic autoindent settings, most of which are pretty much the same as for C.
GNU mkid (from the id-utils project) indexes Lisp like other languages for quickly finding all references to an identifier.
Exuberant Ctags recognizes Lisp files, for jump-to-definition.
People who talk about special tooling for Lisp must be coming from the perspective of Nano or Notepad.
Or else from the perspective of "any programming editor which is not the one I know is 'special tooling', and the one I know doesn't recognize any Lisp file suffixes."
> If the syntax requires special tooling to make it usable
It doesn't. Lisp is perfectly editable in Vim, with basic indentation and syntax highlighting. Lisp's properties make it easy to work with even in that type of basic editor without special Lisp support.
Exuberant ctags indexes Lisp code for jump to definition.
> If the syntax requires special tooling to make it usable, isn't that a sign that the syntax is hard?
Not in this case. Lisp syntax is easy, people lack familiarity.
The only "hard" part is checking that the number of parens are right. But it's not that other programming languages don't have equivalent "hard" problems, like closing the right kind and amount of brackets on C, of whitespace problems in python.
If you make an error in the number of closing parens, and hit return, your cursor will end up in an unexpected place. That's the sign you should take a second look at what you just did.
> If the syntax requires special tooling to make it usable,
In Linux I'm using Geany https://www.geany.org/ with almost the default highlighting. The only change I made was to make the good and bad matches of parenthesis more visible. (In the default, the color of the parenthesis changes from black to blue or red, but it's too difficult to see. I changed it so the color is black but the background is blue or red. It's just a two lines change in the style, not special tool.)
In Windows I'm using WinEdt https://www.winedt.com/ and using again almost the default highlighting.
My recommendation is to mix () with [] and {} as recommended unofficially, instead of using only (). [For my personal code I use a few more [] and {} than the standard. I use [] for long [begin ...] blocks and {} for {define ...}. That helps a lot to find the problems.]
Lisp syntax is simpler than other languages. Unfortunately, simplicity often clashes with conciseness. In Python, you can do neat stuff like `[a[a!=0][:, 8] for a in A]`. It is possible to somewhat replicate the same ease of use using (reader) macros, but Lisps seldom seem to have mature, standard macros for these kinds of stuff.
Between s-expressions in Racket and indentation in Python, this thread really turns out all the people who whine about code that doesn't look like Java.
Racket is trying to make itself an acceptable Python at this time [1]. Also I used to use Racket and the biggest issue was the lack of batteries either were included or I could find a library that would work. But the biggest issue is the community itself [2] and that isn’t going to change any time soon.
How is the Racket community an issue when the "issue" is that one guy bullies (some very particular) people? I've regularly posted reaaaally stupid things on the Racket forums and lists and people (including the core team) have always been super patient and welcoming. I really don't understand that "holier than thou" sentiment. I guess most people really shouldn't go eat in fancy restaurants or seek care from well-known hospitals either, then? Because many well-known chefs, lead doctors, etc. are really no nice people.
But in the comments here and as with most articles about Lisp, the conversation moves to... can newcomers understand Lisp's syntax? My spouse and I gave a talk about this very topic: "Lisp but Beautiful; Lisp for Everyone". https://fosdem.org/2022/schedule/event/lispforeveryone/
There's a lot in there (including demonstrations of representations of lisp which don't have parentheses at all). But the biggest point in the talk is that lisp isn't scary for new programmers... it's scary for experienced programmers. As we talk about in the video, my spouse and I co-ran introductions to programming using Racket aimed at humanities students with no required prior programming experience whatsoever.
What we found was that the students who had no prior programming experience, after just a few minutes, had no problem with the syntax (and DrRacket made it easy enough to pick up the language). But we did have complaints in the class about lisp's syntax... from those who already knew how to program. They already had a frame, and within that frame, lisp's syntax looked alien. Without a frame, students just accepted it and did fine with the syntax... great, even. (Many of them praised its clarity; like any decent lisp editor, DrRacket helps identify the kinds of parentheses-oriented errors that users think they're going to encounter.)
Anyway, I do more work in Guile these days than Racket, but both are really great systems with great communities. The thing I miss most about Racket though is that DrRacket gave an easy entry point for students which wasn't "and now start learning Emacs". (Guix is pretty great, however.)