I think that it helps a lot to have a daily practice of using a language for small things.
In much that same way that many people do the daily wordle or crossword, I do the daily leetcode.
I flip a coin and solve it first in either C++ or Python, then re-write my solution in the other one.
Usually it takes me around 20 minutes to solve it in either language, and 5 minutes to re-solve it in either language.
Recently I decided to start learning emacs lisp. This is an imperative lisp dialect that’s pretty different from scheme, but I think that the particular language doesn’t matter much for this process. I could a bit biased because I do have prior experience with SML and scheme.
I started re-solving the problems a third time in emacs lisp. And I’m still learning but I’ve felt my comfort with the language increase over time, and I expect that if I continue doing this then I will eventually reach parity with C++ and Python.
Currently it takes me about 20 minutes to re-solve a problem in emacs lisp, because I usually have to read documentation and/or look up something new.
> When I think about a programming problem, I think in terms of the sequence of instructions I need the computer to do, and the memory locations that can hold the information the computer needs to track.
You’re almost there. Just stop thinking about the sequence of instructions. Focus on the information half (the values) that you need to produce.
Strange that the post makes no mention of the Little Schemer series, because teaching you to "think Scheme" is exactly what those books do. Some people are put off by the weird style (combination of children's book visuals and socratic logic problem presentation), but they work!
scheme is great, but the dx of some implementations is not. i’m on guile scheme due to guix, and frankly, i’m hating it a quite bit.
stack traces are esoteric and error messages entirely unhelpful,
documentation masquerades as deep but is indeed inconsistent and prosaic, mixing styles of reference, explanation, and how-to willy-nilly, (compare with Dybvig’s The Scheme Programming Language, which is focused and consistent, and it takes no time to get your answers; there’s just no method to guile and guix manuals), i hate it big time,
there’s big gaps in documentation (especially with Guix – there’s literally zero information about `define-record-type*` which is used everywhere in its codebase; admittedly, not scheme related, but still),
the cli requires too much memorization,
most modules are not named, but numbered, ie, instead of something like `(base list)`, you get `(srfi srfi-1)`, so you need to either memorize, or go through the info pages for each procedure you need to import, which means you also need to know the exact names for the procedures you need beforehand,
there’s like 4 ways to define a record, each with a different feature set and incompatibilities,
etc.
these are the reasons i find it hard to use.
to respond to the content of the article, the different neurotype idea is off, because scheme allows you very well to express sequences of operations; the ecosystem of APIs may not cater to this tho. although if it was rephrased into “scheme emphasizes symbolic manipulations, as opposed to operating a machine”, i would agree
I explored migrating from NixOS to Guix, and I also hit the wall with the quirks of Guile Scheme. Architecturally, I find a Guix much clearer API than NixOS and aspire to the peace of mind that would come with a compiled system configuration. When I leaned into LLM's for support and they were substantially less effective at getting me over the line, not just conceptually but being able to close their parens properly. I ultimately decided to bail on the experiment, but it left me sad because my hunch was that if Guix were in nearly any other non-lispy language I'd have stuck through it.
I am only amateur level with scheme but these are common to other scheme implementations. SRFIs are semi-standardized core libraries that work mostly the same in all implementations. I believe the record-type and records generally are one of these SRFIs, and it’s based on a chapter in SICP (several show up as sections of the book where students implement things that would be language feature in a larger env)
All the Lisp implementations seem to have pretty cryptic seeming errors, but it also seems to be very informative to those who know how to parse the call stack and how to expand/dig down in the built in debuggers. This is likely because of the same idea as those record-types, much of the language is built in the same lisp you’re running - so the error messages go a few levels lower level than you would expect. It’s not just a C syntax error type explanation, but rather you get some error about a bad call to a function you’ve never heard of running below that feature we normally associate with being a core function
Scheme was invented as a consequence of Sussman & Steele’s discovery that lexical closures in the lambda calculus had essentially an identical implementation to a fully elaborated version of Hewitt’s actor model.
I do wonder what a language with the same “taste” and minimalism as Scheme but embracing the actor model would look like. Erlang?
Even better if someone could figure out how to harmonize them in the same language: “There are exactly two ways to do it, and they’re interchangeable.”
In terms of modern concurrent actors, in addition to Spritely Goblins there is also Termite, a restricted Scheme with Erlang-like concurrency semantics that now comes with the Gambit distribution.
You can write entirely imperative OO code in Scheme, it just has syntax that's weirder than you're used to. Not everything has to be functional abstractions, syntax macros, or twisty mazes of call/cc. If SICP is too abstruse, give HTDP a try, but if you know other languages, you already know most of scheme.
I understand the challenge, but is Graham (OP?) getting too caught up in how the code ought to look, rather that what it ought to do. I don't think it matters much initially how a piece of work looks as long as it does what's intended. Afterwards it does; particularly if you need to involve other developers, and to them, the idioms looks "strange". I'm not convinced that there's an ALGOL neurotype that's distinct from a LISP(?) neurotype. I think it's a bit of a spectrum like everything else.
I think it's the symptom of inadequate practice rather than some "language neurotype". Consider writing (yeah 2026 I know) a substantial project in Scheme from scratch.
I studied Scheme in CS around 1990, but also found it hard to grasp and to apply to real world problems. And all those parethesis, brrr... :)
The idea that "everying is an object and classes are nothing special" is neat, but it does not fit the way people (okay, I) think about real world problems. So I stuck with Smalltalk/OOP and am stil there :-).
> Obviously an LLM generated the code, but I felt comfortable following along and understood what it was doing, reading and Trusting the Tests. [...] My difficulty is with thinking the way that lets me write Scheme.
There's your problem, right there. Vibe-coding is sabotaging learning before you even start.
You can learn some things by reading good code, but there's no substitute for the exercise of thinking through problems yourself. (Also, an LLM won't necessarily give you good code.)
First learn paint fence, Daniel-san. Not watch third-hand videos spliced together of other people painting the fence, and thinking you'll understand much of anything about it.
> I have the ALGOL neurotype.
Good news! Scheme started as a block-structured imperative "algorithmic" language in the spirit of ALGOL. Just with more parentheses.
Write as if in ALGOL, but using Scheme's comparable syntax and language features. And lots of parentheses.
Don't get confused by CS professors showing you pure-functional features, the metacircular evaluator, recursive programming, syntax extension and language-oriented programming, etc. You can come back to that.
Just start coding ALGOL-style in Scheme. You'll accomplish something in an hour.
Once you see it's easy, and are comfortable with that part, then the next thing you do, to get more idiomatic is one of the following, then do the other one:
* Try to get more functional, by eliminating some of the mutations of variables in your code. For example, if you're using `set!` a lot, can you eliminate them by, for example, making them arguments in a named-`let` recursion. (Or, instead of named-`let`, spell out the recursive functions, like some intro CS professors will want you to do, but that can obscure things that are obvious once you see the named-`let` lexical structure.)
* Try to get more language-oriented, by making a little domain-specific language, maybe with `syntax-rules`, `syntax-case`, or `syntax-parse`.
One more tip, for anyone coming from C, C++, Rust, etc., who may like trying to know the cost of everything: If you get hung up on high-level language features like GC, and not knowing which of a number of ways of doing something, is the right (performant) way, try not to. But if you want an intuition (that might be a lie), imagine that needless mutations or allocations may be more expensive than finding a different way to do it. And each FFI call has very expensive overhead. At one point that I had to make highly performant code, I made a little tool, to help confirm my intuitions: https://www.neilvandyke.org/racket/shootout/ There's also a statistical profiler in Racket now, and you can even (with work) rig it up in production systems, for measuring real-world workloads, which I used to guide optimizing performance of a large and complicated system.
I was the same way (and still am somewhat, I can't get hygenic macros into my head) but due to the differences between Scheme and Common Lisp. What helped me was writing imperative code that Scheme people would surely scoff at, and gradually using more and more Scheme features as I kept writing. Then I refactored the whole codebase to look like the final few hundred lines.
Oh, kinda like how I learned Emacs: use it "wrong" for years, treating it as a sort of weird archaic Notepad++, then gradually discover features, master the keybindings, and learn to program Emacs Lisp over time until my proficiency, and the utility the editor provided to me, grew.
these days i'm seriously considering switching to zed tho
Programming languages, like natural languages, are tools for human beings, not computers. They work around the strengths and weaknesses of a human brain.
It's not a question of being smart or stupid. It's whether the tool fits the task it's applied to and the affordances it gives the user.
Scheme is intended more as a teaching tool than an actual language. Its simplicity is perfect for reasoning about programs. It's less well suited to practical tasks.
About the only really difficult lesson of Scheme is if you use it as a purely declarative language. Imperative features are a natural affordance of the human brain. Working with them is beautiful and alien.
I don't know you, but it seems very unlikely. scheme is a little different because it doesn't really encode or enforce 'standard patterns' that serve as mold that we pour code into. that probably means that its not quite as clear where to start. but at least in r4rs-land its based on a very small number of general simple primitives.
I think it's refreshing change of perspective, and certainly worth pursuing if you're interested at all in in building programming structures rather than just using them. but if its not at all to your taste I wouldn't beat yourself up about it.
There's something very ironic about an article about bouncing off Scheme on a website called 'SICPers'. OT: I think I'm pretty decent at thinking in Scheme, although I don't quite have the hang of continuations. That said, because I like type declarations I use Common Lisp, which allows me to bounce between a more Scheme-like style and a more Assembly-like style however I see fit.
In much that same way that many people do the daily wordle or crossword, I do the daily leetcode.
I flip a coin and solve it first in either C++ or Python, then re-write my solution in the other one.
Usually it takes me around 20 minutes to solve it in either language, and 5 minutes to re-solve it in either language.
Recently I decided to start learning emacs lisp. This is an imperative lisp dialect that’s pretty different from scheme, but I think that the particular language doesn’t matter much for this process. I could a bit biased because I do have prior experience with SML and scheme.
I started re-solving the problems a third time in emacs lisp. And I’m still learning but I’ve felt my comfort with the language increase over time, and I expect that if I continue doing this then I will eventually reach parity with C++ and Python.
Currently it takes me about 20 minutes to re-solve a problem in emacs lisp, because I usually have to read documentation and/or look up something new.
You’re almost there. Just stop thinking about the sequence of instructions. Focus on the information half (the values) that you need to produce.
stack traces are esoteric and error messages entirely unhelpful,
documentation masquerades as deep but is indeed inconsistent and prosaic, mixing styles of reference, explanation, and how-to willy-nilly, (compare with Dybvig’s The Scheme Programming Language, which is focused and consistent, and it takes no time to get your answers; there’s just no method to guile and guix manuals), i hate it big time,
there’s big gaps in documentation (especially with Guix – there’s literally zero information about `define-record-type*` which is used everywhere in its codebase; admittedly, not scheme related, but still),
the cli requires too much memorization,
most modules are not named, but numbered, ie, instead of something like `(base list)`, you get `(srfi srfi-1)`, so you need to either memorize, or go through the info pages for each procedure you need to import, which means you also need to know the exact names for the procedures you need beforehand,
there’s like 4 ways to define a record, each with a different feature set and incompatibilities,
etc.
these are the reasons i find it hard to use.
to respond to the content of the article, the different neurotype idea is off, because scheme allows you very well to express sequences of operations; the ecosystem of APIs may not cater to this tho. although if it was rephrased into “scheme emphasizes symbolic manipulations, as opposed to operating a machine”, i would agree
All the Lisp implementations seem to have pretty cryptic seeming errors, but it also seems to be very informative to those who know how to parse the call stack and how to expand/dig down in the built in debuggers. This is likely because of the same idea as those record-types, much of the language is built in the same lisp you’re running - so the error messages go a few levels lower level than you would expect. It’s not just a C syntax error type explanation, but rather you get some error about a bad call to a function you’ve never heard of running below that feature we normally associate with being a core function
I do wonder what a language with the same “taste” and minimalism as Scheme but embracing the actor model would look like. Erlang?
Even better if someone could figure out how to harmonize them in the same language: “There are exactly two ways to do it, and they’re interchangeable.”
There is Spritely Goblins: https://spritely.institute/goblins/
Official docs: https://docs.racket-lang.org/rhombus/index.html
Collection of small examples: https://github.com/racket/rhombus/blob/master/demo.rhm
There's your problem, right there. Vibe-coding is sabotaging learning before you even start.
You can learn some things by reading good code, but there's no substitute for the exercise of thinking through problems yourself. (Also, an LLM won't necessarily give you good code.)
First learn paint fence, Daniel-san. Not watch third-hand videos spliced together of other people painting the fence, and thinking you'll understand much of anything about it.
> I have the ALGOL neurotype.
Good news! Scheme started as a block-structured imperative "algorithmic" language in the spirit of ALGOL. Just with more parentheses.
Write as if in ALGOL, but using Scheme's comparable syntax and language features. And lots of parentheses.
Don't get confused by CS professors showing you pure-functional features, the metacircular evaluator, recursive programming, syntax extension and language-oriented programming, etc. You can come back to that.
Just start coding ALGOL-style in Scheme. You'll accomplish something in an hour.
Once you see it's easy, and are comfortable with that part, then the next thing you do, to get more idiomatic is one of the following, then do the other one:
* Try to get more functional, by eliminating some of the mutations of variables in your code. For example, if you're using `set!` a lot, can you eliminate them by, for example, making them arguments in a named-`let` recursion. (Or, instead of named-`let`, spell out the recursive functions, like some intro CS professors will want you to do, but that can obscure things that are obvious once you see the named-`let` lexical structure.)
* Try to get more language-oriented, by making a little domain-specific language, maybe with `syntax-rules`, `syntax-case`, or `syntax-parse`.
One more tip, for anyone coming from C, C++, Rust, etc., who may like trying to know the cost of everything: If you get hung up on high-level language features like GC, and not knowing which of a number of ways of doing something, is the right (performant) way, try not to. But if you want an intuition (that might be a lie), imagine that needless mutations or allocations may be more expensive than finding a different way to do it. And each FFI call has very expensive overhead. At one point that I had to make highly performant code, I made a little tool, to help confirm my intuitions: https://www.neilvandyke.org/racket/shootout/ There's also a statistical profiler in Racket now, and you can even (with work) rig it up in production systems, for measuring real-world workloads, which I used to guide optimizing performance of a large and complicated system.
these days i'm seriously considering switching to zed tho
It's not a question of being smart or stupid. It's whether the tool fits the task it's applied to and the affordances it gives the user.
Scheme is intended more as a teaching tool than an actual language. Its simplicity is perfect for reasoning about programs. It's less well suited to practical tasks.
About the only really difficult lesson of Scheme is if you use it as a purely declarative language. Imperative features are a natural affordance of the human brain. Working with them is beautiful and alien.
I think it's refreshing change of perspective, and certainly worth pursuing if you're interested at all in in building programming structures rather than just using them. but if its not at all to your taste I wouldn't beat yourself up about it.