https://sicp.sourceacademy.org/chapters/1.1.7.html#ex_1.6
We're just a few pages in and the original instructive exercise of 'why can't I make my own if out of cond' involves learning the JS ternary operator.
I am also an APL/J fan and this footnote in SICP rings true:
[15] Richard Waters (1979) developed a program that automatically analyzes traditional Fortran programs, viewing them in terms of maps, filters, and accumulations. He found that fully 90 percent of the code in the Fortran Scientific Subroutine Package fits neatly into this paradigm. One of the reasons for the success of Lisp as a programming language is that lists provide a standard medium for expressing ordered collections so that they can be manipulated using higher-order operations. e programming language APL owes much of its power and appeal to a similar choice. In APL all data are represented as arrays, and there is a universal and convenient set of generic operators for all sorts of array operations.
EDIT: > In 2021 the SP kernel was returned to open source forming the basis for the S series of kernels on which this manual is based.
Anyone have a PDF link to a paper about this Fortran analyzer?
Having seen what having javascript as a first language does to first year students I can't understand why this is considered a good idea.
I know Racket has an integrated GUI toolkit, but will this new generation be able to run it on their tablets?
One benefit of the great variety in front-end JavaScript frameworks, often criticized as churn, is that we're more likely to arrive at an abstraction that will be approachable to beginners. I look forward to watching how this generation learns to program.
Nothing prohibits them from learning, "better" languages after you reach them, but first you have to reach them.
Since the original SICP has a permissive license, I've actually thought about doing something similar, but with a more modern functional language.
One of the reasons it was originally written for scheme is that scheme has a small core, simple syntax and can very easily be learned while reading SICP.
More importantly, the type of user that would get put off by using/learning scheme, is probably not going to make it through SICP. Learning just enough scheme is arguably the easiest part of going through SICP.
I think the decision on what language to use in teaching material should be made based on quality and ease use, not popularity of the language.
JavaScript lacks several features that make it apt to translate this book.
What is "accessible" in this context is purely a state and attitude of the intellect.
SICP doesn't try to be accessible, it's explicitely an engineering text book made for engineers. That's even the reason some people have criticized it[1] and wrote another book[2] with the same themes and goals but ditching the excessive math focus. Anyone who can understand SICP's exercises can understand Scheme.
[1] https://en.wikipedia.org/wiki/The_Structure_and_Interpretati...
https://sicp.sourceacademy.org/ (JS version)
https://mitpress.mit.edu/sites/default/files/sicp/index.html (1984 classic)
The in-line editor is a bit intrusive (fills up most of my screen, unlike say eloquentjavascript.net) but it's still pretty neat to have run-able examples. Kudos to the creators.
A ton of work seems to have gone into this -- they have a meta-circular evaluator in JavaScript (and it's not that much longer than the original Scheme)!
list("sequence",
list(list("constant_declaration",
list("name", "size"), list("literal", 2)),
list("binary_operator_combination", "*",
list("literal", 5), list("name", "size"))))
https://sourceacademy.org/sicpjs/4.1.2 ("4.1.2 Metalinguistic Abstraction / Representing Components")This isn't an ideal language for doing this stuff. The Scheme version is completely transparent about what you're doing; whereas this kind of language makes you work through a thick veil of abstractions.
(This line doesn't occur in the Scheme version, but the analog would be (quote (let ((size 2)) (* size 5))). Which would you rather debug?)
You can see there's more negativity in this thread compared to the Python one. HN crowd is pretty hostile toward JavaScript.
Yet JavaScript is obviously a better choice than Python in terms of SICP Scheme substitute. For example:
- JavaScript is arguably a Scheme descendant, or at least have closer blood ties with Scheme than Python.
- The Lambda in Python is just awkward, and SICP is a book about lambdas.
- The Chapter 2: JavaScript use more anonymous object which makes it a better choice, although there are dictionaries in Python it's more favored to use Classes.
- Chapter 3, the Object-oriented Programming part: Use lexical closure to simulate object is just day to day JavaScript. So does dispatch and pattern matching messages.
- Chapter 3, the stream part: In JavaScript world the "functional" streams approach is also very popular.
- As for symbolic programming and such, they're both not particularly good. But that's a problem for all mainstream languages. At least JavaScript has programmable string template.
As you can see many SICP stuff are just day-to-day JavaScript, but not so much in idiomatic Python. Let alone JavaScript is more approachable in almost every platform, and much easier to make interactive tutorials.
Try publishing a dependency for Java. Now try doing it for JS.
Everyone was able to do highly concurrent workloads. Something that was extremely difficult before Javascript.
It just smells of elitism to me. It's a wonderful language that brought the async await system to everyone.
Npm is light years ahead in execution and usage. Python distribution is.... interesting with eggs & poetry
In a very real sense, there is no idiomatic way to write complex lambdas in Python. What you would do instead is define a named function and pass it in as an argument. This is fine but definitely more complicated.
- Gets very messy when nested
And SICP is a book about learning computer science by abusing lambdas.
A
false != x
test failed for a zero-valued x; had to make it !==. WTF? I never want false and 0 to be other than different objects.Almost every twist and turn in this language is an imbecillic clusterfuck.
I had one instance of a if (foo.bar = 0) typo; no warning from the implementation. Why do we want stupid C mistakes in a higher level language, without the C fixes for them?
list.forEach(function (item, i) {
...
});
makes my eyes bleed. JavasSript borrows the syntax of languages geared toward a form of computing at which JavaScript is poorly suited for; when you're combining functions together, it looks like dog's breakfast.To get a value out of a switch statement, you have to wrap it in a function that is immediately called?
function () {
switch (WTF) {
}
}();
the switch statement has the idiotic break with fallthrough found in C.One redeeming feature: A || B || C ... works a lot like (or A B C ...).
Unfortunately, this is required for the simple task of incrementing a nonexistent dictionary key from zero:
dict[key] = (dict[key] || 0) + 1;
this calls for two dictionary key lookups. You want this sort of thing in a single operation.Oops! In the following, j isn't lexical:
for (let i = 0, j = 0; ....
I copy and pasted something like this from a StackOveflow answer into a recursive function and spotted that the j in a recursed frame was messing with the j in outer frames, there just being one j.What is "function scope" and why even have something so idiotic? Just let, or GTFO! C has function scope for goto labels, that's it; why introduce something like that.
Appending together Lists has functional semantics, like Lisp:
a.concatenate(b) // new thing is returned, a stays the same, so you must do a = a.concatenate(b).
But a.push(b) // mutates a in place.
Here is a shitshow, curently with a fitting number of upvotes: 666.https://stackoverflow.com/questions/881085/count-the-number-...
I accidentally wrote
if (foo.bar.indexOf[key] < 0) { code }
code was silently skipped even though foo.bar is an empty list. No diagnostic, nothing. Oh, indexOf is a function, which is an object. Every damned object is a dictionary with properties you can access as strings with square bracket indexing, and that is always safe: it yields undefined if the property is not found.But, they disappear and are mostly non-issues once you use the alternate system/language regularly for a while. Some I think you will come to see as benefits, sensible, or find that there are trivial workarounds.
One quick improvement. The more commonly used contemporary syntax in JS for something like your list.forEach example is: `list.forEach((item, i) => ...)` —writing out the full `function` keyword is indeed an eyesore :)
I don't think I'm correct here. However, be that as it may, in my code, when I moved that second variable before the loop and initialized it there with a let, the behavior somehow changed.
Why do you try to code JS without reading about the language? It's the only language where people consistently try this kind of thing.
> false != x
Referential equality operator is `!==` (which isn't so different from lisp where you have eq, eql, equal, string=, etc). Fun fact: Eich didn't have type coersion in the language at first. It was added at the insistence of developers (something he regrets, but Microsoft forced this to stay in the spec)
> function () { switch (WTF) { } }();
A lot of languages don't make switch statements into expressions. The fact that JS can do this via a function while most other popular languages cannot do it at all is a benefit if anything (there is a proposal to allow these to be expressions). More generally, prefer this
let foo
switch (abc) {
case "blah":
foo = 123
break;
}
> the switch statement has the idiotic break with fallthrough found in C.It actually gets this from Java. Eich was told to make the language look like Java and he did.
> dict[key] = (dict[key] || 0) + 1;
Your code is potentially bad. `||` uses "falsey" values. If the key is zero, the left-hand side will return false forcing the right-hand side. Use `dict[key] ?? 0` instead as it will only return the right-hand side if the left side is `undefined` or `null`.
If it is a pure dictionary, you are using the wrong construct too. Use `Map` instead
let dict = new Map()
dict.set((dict.get(key) ?? 0) + 1)
> this calls for two dictionary key lookups. You want this sort of thing in a single operation.You get a value rather than a reference. I believe the JIT can optimize this pattern. If you are really fixated on using a reference, store an object with a value instead so you can grab and keep an object reference.
> What is "function scope" and why even have something so idiotic?
In lisp, you'd use `(let ((foo 123)) (print foo))` to set an explicit scope. ES4 proposal had a let block which I think would have been a better idea than the current stuff. Function hoisting generally occurs in other languages one way or another, but is hidden from the user.
> for (let i = 0, j = 0; ....
Yep, this is one more reason why the correct fix was a let block with `let (i=0, j=0) for (; ...)` or 1et for (var i=0, j=0...`
> Appending together Lists has functional semantics, like Lisp:
Array.prototype.concat goes back to at least the ES3 spec as does Array.prototype.push. At the time, arrays were either implemented as hashtables or linked lists. My guess is that adding a single entry to the hashtable only occasionally caused a resize while concatenating N lists was almost guaranteed to do so. I think they decided that it was easier for implementors to just create a new reference.
Since ES5, most new array APIs have favored returning new lists for everything. I think there's a case for making new versions of those older APIs that behave immutably.
> if (foo.bar.indexOf[key] < 0) { code }
This could happen in any language with a methodNotFound handler. For what it's worth, `if (foo.bar.contains(key)) {code}` would be the better way of writing it. I think Typescript would also catch this (though I'm not 100% as I don't remember ever having made that particular mistake).
> Every damned object is a dictionary with properties you can access as strings with square bracket indexing, and that is always safe: it yields undefined if the property is not found.
Garbage In; Garbage Out. At least it handles that garbage in a safe manner. What more could you ask from a dynamic language?
It was designed to be easy for students to implement with relatively easy parsing rules. It is fully functional, but allows mutations. It also has an actually sound type system.
That would be very nice.
Everybody's dealt with that one asshole* who overdosed on category theory and proceeds to write Haskell in Python for the next two years.
* It's me, I'm that asshole.
Guilty here too, just not in python.
In langs with proper TCO (like scheme and lua), recursion is a beautiful and practical way to solve problems.
Yes, I pre-ordered.
For those complaining about it being in JS, probably didn’t read SICP as it wasn’t about LISP. This isn’t about JS.
That's precisely why it shouldn't be in JS.
Edit: to clarify, javascript is full of random warts that you have to know about in order to use it effectively, that's why I think a language with less such warts would be more suitable for someone that wants to try out and use the ideas book introduces with caring too much about the language.
that one isn't too surprising because "putting scheme in the browser" was quite literally what Eich set out to do
"As I’ve often said, and as others at Netscape can confirm, I was recruited to Netscape with the promise of “doing Scheme” in the browser. At least client engineering management including Tom Paquin, Michael Toy, and Rick Schell, along with some guy named Marc Andreessen, were convinced that Netscape should embed a programming language, in source form, in HTML. So it was hardly a case of me selling a “pointy-haired boss” — more the reverse.
Whether that language should be Scheme was an open question, but Scheme was the bait I went for in joining Netscape. Previously, at SGI, Nick Thompson had turned me on to SICP.
"SICP – Upcoming JavaScript Edition - https://news.ycombinator.com/item?id=27737942 - July 2021 (2 comments)
Structure and Interpretation of Computer Programs – JavaScript Adaptation - https://news.ycombinator.com/item?id=21822903 - Dec 2019 (180 comments)
SICP JavaScript Going Public - https://news.ycombinator.com/item?id=21779397 - Dec 2019 (1 comment)
SICP translated to JavaScript - https://news.ycombinator.com/item?id=6385617 - Sept 2013 (107 comments)
We moved on. Assuming we have completed nand2tetris[https://www.nand2tetris.org/], we should instead be focussing on something like HtDP[2], or maybe like how they do at Brown using Pyret[3] or my favourite, how they teach programming at CMU - C with training wheels[5][6]
SICP as a second course, will then be fun to those passionate about mariad applications in the midst of innovation decades.
Javascript (and sometimes Python) won't give you the insight these courses were designed for. To learn programming? Absoutely NO.
[1]http://www.aduni.org/courses/ [2]https://news.ycombinator.com/item?id=18890417 [3]https://www.pyret.org/ [4]https://www.cs.cmu.edu/afs/cs/local/sml/common/smlguide/inde... [5]https://news.ycombinator.com/item?id=15499160 [6]https://bitbucket.org/c0-lang/docs/wiki/Home
It has a TON of syntactit niceties today. Most of the warts are no longer relevant (here's to a future "use strict 2" mode that eliminates the really bad type coercion bits).
It hits a very pragmatic center where you can do OOP, but top-level functions are also possible (looking at you Java).
JS also has good functions. Closures that don't require spelling everything out, first-class functions that can be passed around to other functions, anonymous functions, etc. Outside of currying, it has almost everything you'd want functions to have. That's something sorely missing in languages like Python (lambdas), Ruby (proc/lambda/block mess), PHP (no implicit closures), Java (no first-class functions), etc.
Here is an old screenshot of the interpreter that I was able to build:
https://paradite.com/wp-content/uploads/2013/10/interpreter....
Et tu, Brute??
- For how to actually program: How to Design Programs [1]
- For how to write programming languages: Essentials of Programming Languages [2]
- For how computers actually work: The Elements of Computing Systems (nand2tetris) [3]
I am a huge fan of How to Design Programs. It does a better, more thorough job of teaching the "core skill" of programming SICP or anything else I've come across. Its text is better paced (although very long), more approachable, and more focused on teaching how to program (as the name suggests). On top of that it has an absolutely massive number of exercises that do not require an interest in basic number theory to enjoy.
You will know how to actually solve new problems with programming when you finish HtDP (or are even part way through).
The really interesting parts of SICP are the 4th and 5th chapters any way right? Well, Ch. 4 (about "metalinguistic abstraction" or how to modify your interpreter to give your language different properties) is incredibly densely packed with information. It covers an interpreter for all of this in about 100 pages:
- lazy evaluation by default
- nondeterminism
- aka search as a language primitive
- the basics of continuations (but not accessible to the user)
- propositional logic - aka declarative (what is) vs imperative (how to) programming
It's a great appetite whetter, but it'll leave you wanting more.If this stuff interests you Essentials of Programming Languages is a much better resource. It follows a similar style to the approach in SICP. You are walked through building interpreters for increasingly more complicated languages. Here are some brief descriptions of some of features you'll learn to add to the LET language (a very simple purely functional language that serves as the core, you'll also write this too):
- Mutually recursive procedures
- Static code transformation to use lexical addressing
- Mutation (and how to perform call-by-value, call-by-ref, and call-by-need languages)
- Continuation-passing vs Environment-passing interpreters
- Advanced control structures like exceptions and threads
- Static types and type inference
- Typed modules (I've not seen any other introductory text do this)
- Object-orientation
It contains a plethora of great exercises that ask you to extend the languages in interesting ways or to reflect more realistic demands of a language.
For chapter 5, Register Machines, I think we all know that the first half of Elements of Computing Systems (colloquially nand2tetris) is a great resource for that. The second half is excellent as well, but it's outside of the SICP scope.
Summary:
;; Page Count 1374 = (+ 792 410 (/ 344 2))
In total I just recommended 1374 pages to replace 657 pages in SICP, so it's double the effort. And to be honest, if you're motivated enough to read all that then, you'll probably still read SICP if for nothing else than historical interest. If you start with SICP though, you'll still get a lot out of all the recommendations though!
[1] HtDP: http://htdp.org/2021-11-15/Book/index.html
[2] EoPL: https://eopl3.com/
[3] nand2tetris: https://www.nand2tetris.org/
Reusing the SICP name is just a marketing tactic to sell something different and probably less special.