Hacker News new | ask | show | jobs
by dswilkerson 730 days ago
I used to be a teaching assistant for CS 61A (intro to programming) at Berkeley teaching from this book with Brian as the instructor.

One of Brian's primary points is the following:

> Scheme ... has a very simple, uniform notation for everything. Other languages have one notation for variable assignment, another notation for conditional execution, two or three more for looping, and yet another for function calls. Courses that teach those languages spend at least half their time just on learning the notation. In my SICP-based course at Berkeley, we spend the first hour on notation and that's all we need; for the rest of the semester we're learning ideas, not syntax.

Bullshit. Again, I was a TA for this course. You do not spend the rest of the semester on ideas, you spend the rest of the semester on the students being very confused.

This "everything looks the same" property of Scheme and of all LISP-like languages is a bug, not a feature. When the semantics is different, humans need the syntax to be different. In contrast, LISP/Scheme make everything look the same. It is quite hard to even tell a noun from a verb. This makes learning it and teaching it hard, not easy.

Brian is selling a fantasy here. If you think Scheme is so great, look at this nightmare of examples showing the various ways to implement the factorial function in Scheme: https://erkin.party/blog/200715/evolution/

All of this "abstractions first, reality second" agenda is just a special case of what I call "The Pathology of the Modern": the pathological worship of the abstract over the concrete. Everything modernism touches turns into shit. I am done with living in modernist shit and I hope you are too.

10 comments

I wouldn't have spoken up except for this comment. As a freshman, I took 6.001, the MIT course that Structure and Interpretation of Computer Programs was based on, and I loved it. As a graduate student, I taught 6.001 three times, twice as head TA under Prof. Sussman and once under Prof. Abelson. In addition to helping make up problem sets, quizzes, and exams, my responsibilities included teaching seven or eight one-hour, five-student tutorial sessions per week as well as teaching a forty-student recitation section once per semester. I graded assignments as well. My point is that I have a lot of experience with what students found challenging in the course.

Prof. Harvey's claim rings completely true to me. Students understood the syntax quickly, and spent little time on it. It was not a point of frequent confusion. There were plenty of difficult concepts in the course, but the details of the programming language were not, for most students, among them.

Students who already had programming experience when they started the course often had more trouble than inexperienced students, but mostly because they had to unlearn imperative habits since the imperative features of the language, except for I/O, weren't used until late in the course.

SICP covers a huge breadth of material, from basic computational ideas to algorithms and data structures to interpreters and compilers to query languages to concurrency, and does it in an entertaining and challenging way. Even decades later, I find myself pulling ideas from it in my daily programming work.

I worked at Google for almost twelve years, and I can't count the times I found myself muttering, when reading a design document, "I wish this person had read SICP."

I'm certainly biased, but I would encourage anyone who would like to become a better software engineer to read SICP and study it carefully. Take your time with it, but do read it.

I've had an introductory Scheme course in a smaller university, and have experience designing data structures, creating parsers & interpreters, and with multi-threading and networking.

I was never one to really dig lisp. I prefer the structure and the groundedness of a statically typed systems language (I mostly do systems work). But I took on reading SICP in the hope of finding something new and interesting, and to level up my skills. However, I got bored by the it. Probably made it through more than half of the book.

It's a bummer because I'm left with the feeling of missing out. Am I not worthy or too obtuse to get what's so great about the book? Or maybe I am in fact not the target audience, having too much practical experience that the book doesn't seem worth my while.

If you're comfortable writing interpreters you've probably already picked up on most of the "big ideas" SICP is trying to teach. It's a very good introductory book, but it is still just an introduction.
Honestly GP is making two very valid points though.

Something that Clojure does is differentiating between () = lists of calls, [] = vectors (the go to sequential data structure), {} = maps. This definitely helps the eye to see more structure at a cursory glance. It has a little bit more syntax compared to Scheme, but the tradeoff seems to be worthwhile.

Secondly, I think it's very healthy to be wary of indirection and abstraction. I'm not sure if I agree with the tone and generalization about modernism, but I think there's a burden of proof, so speak, when it comes to adding abstractions, especially in the long term.

I think Scheme works well for the kind of conceptual overview the course is trying to provide. I think there is something to the argument that Scheme syntax is not ideal for readability of larger programs, but I would wager that the bigger reason some students find SICP confusing is the same reason it blows others’ minds - the whole approach is at a higher level of abstraction than most “intro to programming” classes.
Yes, I agree these are two good points. I also experienced teaching SICP and would say the overall position of the GP is incorrect and results in a less profound understanding of programming.
> Take your time with it, but do read it.

And do the harder exercises. Really do them, not just read and tell yourself you understand how to do that one and move on.

Thanks for speaking up. At this point no one is really presenting any evidence so it’s a necessary evil to offset the Lisp slander even if it is, like the parent comment, not much more than an appeal to authority / popularity.

Syntax is absolutely neither natural nor unnatural, by nature, to humans, but it’s a fact that fewer symbols to memorize is easier than more symbols to memorize. The problem is a failure to launch. Some people never truly understand that it’s not just syntax, it’s semantics. Data is code, code is data. That’s why it all looks the same. This artificial distinction in “C-like languages” is more harmful for the enlightened programmer than it is helpful. Unfortunately not everyone that reads SICP experiences enlightenment the first time (or ever, I guess?)

Information hierarchies are empirically important and are an essential part of communications design. Uniform syntax makes information hierarchies harder to parse, because the boundaries around different types of information all look the same. It's the same reason we have different sized headings, bold text, etc. They are distinct markers.

So yes, fewer symbols means easier memorization, but you could take that to the extreme and you'll find that binary is harder to read than assembly.

I think Lisp is really elegant, and the power to treat a program as a data structure is very cool. But scanning Lisp programs visually always takes me a little more effort than most other languages.

My impression has been that people complaining about Lisp's parentheses are complaining about them because they are the most obvious difference between Lisp and other languages, but that they're not what is actually causing them problems. It's the functional approach, where everything is in some sense just algebra, that really throws people off. Of course I can't see inside people's minds, but whenever I discuss this with someone for long enough, that's the impression I get.

Parentheses are just a scapegoat.

People complaining about Lisp parentheses mainly just trolling, not actually working with any kind of Lisp dialect at all.

Traditional Lisps are not functional, but multi-paradigm.

Working with lists is functional though, in that operations that build larger lists out of smaller lists or atoms return a value that you must capture. You don't create an empty list with a persistent identity, which you treat as a bag. New programmers are encouraged to write "pure Lisp", which is a term that denotes list manipulation which treats cons cells as immutable (or any other objects you happen to be using, but mainly those).

Javascript treats character strings similarly the way traditional pure Lisp treats lists. You cannot mutate an existing string to add characters to it, but perform arithmetic on strings to produce new strings. Yet that doesn't prevent the adoption of Javascript. People are cheerfully doing text processing in Javascript in website after website after web application.

The most popular Lisp currently is supposedly Clojure and it is much more doggedly functional than traditional Lisps like Scheme and Common Lisp.

Nope; the parentheses thing is just pure trolling by mainly non-users.

Anyone who actually uses some kind of Lisp could easily write comments that target true weaknesses.

I suspect there is a group out there who has genuine problems with the parentheses, due to cognitive problems like dyslexia and ADHD and whatever. However, I don't see how they can do well with any programming language syntax. Show me what you do use, and how far you've gone with it before I can take you seriously about the parentheses.

I really want to like that idea you're describing, however I've found in practice, there absolutely is a practical difference between code, data and types. I mean, they literally live in different sections of a process. If you design a program that runs on a real machine, and you spend a lot of time thinking what the program should do, how it can put the limited resources of the system to good use -- you absolutely need to think about code and data separately. Mostly think about data, really.

The one area where "code is data" remains a nice idea in my mind is for metaprogramming. And whenever I've done more metaprogramming than small doses, I've come to regret it later, no matter what the language was. (Small doses of metadata can be done even in statically typed, AOT compiled languages without RTTI).

The reason is I think, just basic data structures and simple procedures built in to a language allow you to express most everything you need, in a very direct manner. The number of distinct concepts you come up with as a programmer can usually be directly defined in the base language. Metaprogramms won't create new concepts as such, it's only code run in a different phase. There is definitely a case for generic/templated data structures but it seems it's best to use them sparingly and judiciously. Be wary of them duplicating a lot of code, fatting up and slowing down your system at compile time and/or runtime.

Upvoted for an interesting take, even though I disagree with some of it.

I took 61A from bh. Personally, I agree with bh's statement that you quoted. Where I encountered difficulty was applying the ideas in a different context (e.g. C or Java). Brian spent time addressing this precise difficulty (in the last lecture or so), but it still wasn't enough for me.

I do heartily agree with you calling out "the pathological worship of the abstract over the concrete". Knuth's Concrete Mathematics was also bucking this trend (e.g. https://youtu.be/GmpxxC5tBck?si=tRHQmuA4a-Hapogq&t=78). I'm curious, once you came to this opinion/realization, how did your teaching/learning change?

Just an anecdote.

I took CS61A by Brian Harvey in 2009. I loved the course and I actually spent very little time learning the syntax and most of the time learning the concepts.

So I fully agree with Prof. Brian Harvey here.

Does it beat OO Java classes with students crying ? or months spent on warning kids not to mutate an iterator or else you're gonna cry again ?

Mostly kidding but different paradigms bear different pain points it seems.

Oh and lastly, the let-us-care-not-about-syntax is also an argument at Brown edu (krishnamurti and his team IIRC)

That said, I'd be curious to hear what your students had to say about scheme confusing traits.

I taught both SICP and Java, and I can confirm Java was far more confusing to students. Classes vs instances, inheritance, polymorphism. Why was everything a class? Don't I just want the computer to do something to some input?
And the public static void main and then endless conversations about packages, and public/private fields, that will backfire very pragmatically (at the time) unit test frameworks didn't have a way to call private methods ... Ironically by the time you're done with the basics, nobody has stamina anymore to learn anonymous inner classes.

The thing is, somehow syntax and some forms of abstractions cast a magic spell on most of the population (at time myself included) .. it's your mental interface to the semantics, so you want more syntax to be able to have more abilities, but syntax composes badly.

At least to me that's why I enjoyed lisps / lambda calc, it reduces the domain into a more homogeneous space, suddenly more things are possible with less. Although it seems that the mainstream rather enjoys doing simple thing with verbose tools (it does looks like you're doing a lot of work with a lot of advanced terminology) than solving hard problems with APL oneliners (hyperbole).

Different psychologies ?

I don’t think OO should be taught to students who aren’t already familiar with structs and passing functions around.

If those two things are already well-understood, the nature of OO as a some syntactical sugar and a couple lookup tables is readily apparent.

Without that background, the terminology seems weird and arbitrary and the behavior magical.

You could also portray this as yet another case of theory trumping practice, which is also symptomatic of modernism.

The idea that a language based on a small, elegant set of composable primitives is inherently better for programming in the large as well has not been borne out in practice.

I won't dispute your experience, but for me the point did hold true. SICP was my first introduction to anything Lisp-like, and by that point I'd done C/C++, a bit of Java, quite a bit of Perl/Python, and of course BASIC.

And I was really surprised how quickly and effortlessly I picked up the part of Scheme taught in the book. Faster than any language I had encountered thus far - Python included.

A funny thing to me is I took CS 50 in '85 or '86 (CS 50 and CS 55 were later split into the CS61X series), and instead of Scheme we used Logo for functional programming.... using Brian Harvey's textbook. He was not teaching the course that semester.

At least part of the goal of CS 50 at that time was to explicitly weed students. They didn't want undeclared students to waste a whole lot of time on CS only to find out they were not going to be accepted into CS. Instead, they went through one hard course to find out. Perhaps that explains why some of it was overwhelming to some students?

> Bullshit. Again, I was a TA for this course. You do not spend the rest of the semester on ideas, you spend the rest of the semester on the students being very confused.

I was a TA on an SICP course at a UK university, disagree with you. The students weren't confused, the simple syntax really helped and, because all the students had good maths knowledge, a functional style was a lot more intuitive than imperative.

FYI, the course has since been replaced with Python programming.

> It is quite hard to even tell a noun from a verb

What?

Unless the list is quoted or something, the first item after the opening paren is always the "verb", yes?

There's nothing stopping any other item from being a verb, no? (Not the verb, but a verb.) Anything involving higher order functions?
In the context of the verb, everything else is a noun. When you understand what the verb does, then you can care about the difference between a verb and a noun.
Certainly, but the original quote was "It is quite hard to even tell a noun from a verb" (emph. added), and this is correct, you can't tell whether an identifier refers to a function or variable in Scheme by sight alone. This seems desirable if one wants first-class functions, and is very much an intentional choice for Scheme, but it can admittedly be more difficult to build up a mental model of new code if you have no idea what's a variable and what's a function (esp. amidst an already-difficult-to-grok sea of parentheses).

Notably, this isn't intrinsic to Lisps - Common Lisp uses a different syntax and namespace for function names and variables. My understanding is that Scheme et al's decision to merge the namespaces/syntax was not without controversy in the Lisp community (the Lisp-1 v Lisp-2 debate).[0]

[0] http://www.nhplace.com/kent/Papers/Technical-Issues.html

The only "verb" is the open paren. Other languages just make this simple and fundamental rule way more complicated.
> you can't tell whether an identifier refers to a function or variable in Scheme by sight alone

Nor in C. Nor in JavaScript. Nor in Java. Nor in...

I mean, what is "foo"? Could be the name of a function. Could be a char variable. Could be a double precision float. Could be a pointer to an array of pointers to functions returning doubles. Without going back to its definition (or prototype, for function arguments) you can't tell, much the same as you can't tell in Scheme without looking for the matching define or set!

I feel like I must be missing something here. What?

> I feel like I must be missing something here. What?

If I were to hazard a guess at what the original poster was getting at, it might be the culture of those languages, combined with the power of Lisp to redefine its own syntax.

Lispers value concision, love higher-order functions, and love wrapping things in other things to reuse code, so you might easily see a non-trivial stretch of code without a single function call you recognise. Imagine code where the smallest chunk look something like (dq red foo '(hat n (ddl m) f)). There could be anywhere between zero and eight functions in that snippet, or any one of those might be a macro which re-orders the others in any way (or perhaps its parents include a macro, in which case you really can't assume anything about how / if this stretch is executed at all), it could be a wrapper around something that in other languages would need to be an operator (perhaps it's an if statement?), etc etc.

It's absolutely true you can shoot yourself in the foot in any language, but Lisp is unusually good for it. It's part of its power, but that power comes with a cost. Imagine talking with someone that had a proclivity for making up words. In small doses, this might be fun and save time. In larger doses, you begin losing the thread of the conversation. Lisp is sorta like that. It might seem flammorous, but before you prac it grombles, and you plink trooble blamador!

Why would the students be confused? By what exactly?

> This "everything looks the same" property of Scheme and of all LISP-like languages is a bug, not a feature.

But you are mixing up things here. There are things that look different. Most things in Scheme can be understood as function calls and match that syntax, but there is different syntax for define, let, cond, if, and others. Not everything looks the same. What you might actually mean is, that everything is made of s-expressions. That is actually very helpful, when you work with code. It makes it very easy to move things around, especially in comparison to languages like Python, with significant whitespace indentation.

> When the semantics is different, humans need the syntax to be different.

I learned multiple languages before Scheme, and they did leave their scars, but I find, that I do not need syntax to be that much different. Maybe I am not human.

> In contrast, LISP/Scheme make everything look the same. It is quite hard to even tell a noun from a verb.

Is that a feature of the English language? I have rarely had this issue in Scheme. Perhaps it is because I think a lot about names when naming things.

> This makes learning it and teaching it hard, not easy.

Maybe I only had bad classes and lectures before reading SICP on my own, but I found, that I learned much more from it than most teaching before that was able to teach me.

> Brian is selling a fantasy here. If you think Scheme is so great, look at this nightmare of examples showing the various ways to implement the factorial function in Scheme: https://erkin.party/blog/200715/evolution/

And what exactly is your criticism?

That there are many ways of writing the function? That is a property of many general purpose programming languages. For example we could look at something like Ruby, where it has become part of the design to allow you many ways to do the same thing.

Or the richness of programming concepts available in Scheme? Is that a bad thing? I think not. You don't have to use every single one of them. No one forces you to. But am I glad to have them available, when have a good reason to use them.

Surely you are aware, that the page you link to is at least partially in jest?

> All of this "abstractions first, reality second" agenda is just a special case of what I call "The Pathology of the Modern": the pathological worship of the abstract over the concrete. Everything modernism touches turns into shit. I am done with living in modernist shit and I hope you are too.

I don't know where you got the idea, that SICP lauds "abstractions first, reality second". This is not the essence of SICP. SICP invents abstractions, once it shows, that some previous approach was not sufficient. A good example is the whole "develop a package" thing, where piece by piece the requirements grow and data directed programming is introduced.