Rendered at 23:08:48 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
senkora 1 days ago [-]
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.
retrac 1 days ago [-]
> I could a bit biased because I do have prior experience with SML
You're probably under-weighing this factor.
The average programmer looks at SML syntax and cannot make, pardon the expression if you will, heads or tails of it.
Indeed, I'd argue the average programmer still considers recursion an advanced topic.
hibbelig 1 days ago [-]
Recursion. Very interesting. My daughter didn’t know much about programming, then started CS. The first semester language was Ocaml and they of course used recursion quite heavily and she’d didn’t know it was supposed to be complicated. The second semester had assembly, C and Java and suddenly it was a problem. I had to remind her that she’s had already done it in the first semester.
ajdlinux 17 hours ago [-]
My CS degree started first semester with Haskell, with a few weeks of Java tacked on the end. It was interesting to see some of the students who had prior programming experience have to work a bit to adapt their way of thinking to Haskell, while some of the students with no programming background at all were perfectly happy writing code in a language that made reasonable sense if you were good at maths, then had to work harder once we swapped to Java.
chamomeal 22 hours ago [-]
Sounds like a sick CS department. We had to do Java. The whole first year was just OOP day in and day out. If I’d never seen python I really would have thought all programming is OOP, and probably would’ve dropped it for good
gucci-on-fleek 18 hours ago [-]
> If I’d never seen python I really would have thought all programming is OOP
But isn't Python object-oriented? I mean, it's arguably more object-oriented than Java, since unlike Java, everything in Python is an object and it supports multiple inheritance. It's totally fair to not like Java, but if you like Python, then I doubt that OOP itself is what made you dislike Java.
Izkata 18 hours ago [-]
Python is an object-oriented language in the broader meaning, but object-oriented programming is a particular style that java pushes you into, that python doesn't. It doesn't just mean "everything is an object", it's more like "everything is done in terms of objects that contain data and functions (methods)". Python has objects and could be done in that way, but a more basic imperative style with functional programming elements is more common, where objects are just one component instead of the driving force.
mbac32768 19 hours ago [-]
After spending years on end doing functional programming I used to stumble when looking at for loops until I got used to thinking that way again. It's funny how different the skills are.
meken 1 days ago [-]
Nice to meet someone else that does the daily leetcode! It is really a nice feature - I wish every online judge website would add it.
Bonus - Racket is an accepted language on leetcode ;-)
daemin 20 hours ago [-]
Do you find yourself programming a solution in the second language in the style of the first? Or are they both similar enough to you that it is effectively the same style of solution?
senkora 4 hours ago [-]
I typically do the same algorithm for each solution, but I do try to write it in an idiomatic way to each language.
I usually look at the editorial after the first solve to see if I missed any tricks or other approaches, and sometimes I’ll write the second solution differently if there was something interesting that I missed.
arikrahman 1 days ago [-]
It's advantageous in the real world as well with Guile Scheme being used for Guix.
throwaway27448 1 days ago [-]
How did you arrive at C++ and Python? Yes I'm aware they have many structural and aesthetic differences, but why would you not choose a language with a different paradigm—functional, logical, even procedural, etc?
senkora 1 days ago [-]
Good question. I chose them because they are the languages that I use professionally and would choose for technical interviews. I expect to be asked to solve problems in either language in any given interview.
Technical interviews are different enough from day-to-day work that I still find it valuable to practice in them.
epolanski 1 days ago [-]
I hope you really do it more as enjoyment (like people do crosswords) with just a very secondary benefit of technical interviews, because otherwise I can't but find this rat race obsession super sad.
Pay08 16 hours ago [-]
Relatedly, I've noticed the strange infatuation that a lot of C++ programmers have with Python specifically. I have no idea why that is.
aldanor 11 hours ago [-]
Both have `class` keyword
Pay08 8 hours ago [-]
So does JS, yet it's not the scripting language du jour in the C++ world.
1 days ago [-]
sillysaurusx 1 days ago [-]
Where do you find a daily leetcode problem? Is there a website somewhere that publishes them?
meken 1 days ago [-]
They are published on the leetcode website. Here's a screenshot showing where to click [1].
LeetCode does have an "unofficial" API to get the problem of the day. Should work for previous days as well. This code worked last I tried [2].
It's a feature of the leetcode.com website. I actually can't find a URL that will directly link to the current day's problem, but you can open https://leetcode.com/problemset/ and select the current date in the calendar widget (at the top right on desktop, or by clicking the green calendar floating action button at the bottom right on mobile).
You can view the daily problem without logging in or creating an account.
Fair warning: today's daily problem is a "hard" difficulty and it is often worth skipping those or peeking at the solution. They can take a while to solve (often at least an hour) and/or rely on unusual tricks or data structures. I haven't solved this one yet so idk exactly what its deal is.
twoodfin 1 days ago [-]
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.”
mepian 1 days ago [-]
> I do wonder what a language with the same “taste” and minimalism as Scheme but embracing the actor model would look like.
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.
jolux 1 days ago [-]
What do you find insufficiently minimalistic about Erlang? It’s not exactly a shaggy dog of a language.
GregDavidson 2 hours ago [-]
I started with Fortran 2 which has subroutines which don't behave as black boxes. It took me months of frustrated study to understand procedure calls in decent languages as delegation. That opened up the world of high-level computing. Later I would teach this using problems that were a good fit for "recursion". Recursion is not a feature, it's just an obvious pattern of the more general and important nature of delegation. While recursion is occasionally a useful technique, it's tremendously valuable as a tool for learning how to think about procedures as black boxes!
vnorilo 1 days ago [-]
If solutions come naturally to you in OOP, why not just roll with it?
Personally I find it easiest do design data flows: think about what we need in order to compute the result. The less I have to think about state the better. Functional patterns fall out and it feels simple and easy to me, so I do it. I'm way over the phase I thought that's somehow cooler than the OOP folks, however.
iainctduncan 1 days ago [-]
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!
fredrikholm 15 hours ago [-]
I love that book so much, it helped me grokk the more lambda calculus aspect of FP more than anything.
A few chapters in I belly laughed when it dawned on me that it's going to be recursion until the cows come home. The authors insistence on working through the steps is endearing and comical. Great book to come back to every once in a while.
SoftTalker 1 days ago [-]
I struggled with The Little Lisper (as it was called at the time) in school but as I approach the other end of my career I've given some thought to working through it again. I think it would come a lot more easily now. Though I looked at Haskell maybe 10 years ago and never got traction with it.
Functional or declarative programming styles appeal to me, and I love what I've learned of Erlang but never really had a job where I could use it. Used SQL a lot and quite like it as well.
funkaster 1 days ago [-]
I've been forcing myself to stay within the CHICKEN ecosystem for the past 6 months. So far, the whole ecosystem moves really slow, but CHICKEN is such a good design that adding/rolling your own library for whatever you need is so simple. I ended up creating a bunch of eggs (CHICKEN libs) and even rolling my own web framework with its own ORM and background job processing that I rolled out to prod (1000+ users for the past 6 months or so, not a huge demo but it proved that it can scale/be good enough for my projects).
Best of all, is that the syntax grammar is so simple, that a simple CLAUDE.md + a few review agents lets me move super fast using AI. I spend most of my time anchoring on the design/plan and let AI write the implementation for most of the code.
volemo 19 hours ago [-]
But are you learning programming in Chicken or in Claude?
anthk 15 hours ago [-]
Neither. These people don't realize that they are learning ever less skills than not touching Claude ever.
anthk 16 hours ago [-]
Toss AI. Install srfi-203 and srfi-216 for SICP support. Then at ~/.csirc
With all due respect to the Rhombus authors, this sounds like a downgrade.
gus_massa 5 hours ago [-]
I still prefer S-expressions, so I go to the Rhombus mailing list every few months to give an opinion for or against one feature that I think is interesting.
Anyway, making Rhombus forced a few general enhancements in the Racket ecosystem, like more support for not-s-expression languages in the editor, filters in docs for languages families, and some new "backported" libraries like Treelist that behaves closer to a list in Python.
My main concern was how to make good macros without s-expressions. There is a nice video by Matthew Flatt in RacketCon 2023. The first 6 minutes and 20 seconds are internal stuff, so skip to the 380s that I added in the link: https://www.youtube.com/watch?v=OLgEL4esYU0&t=380s He takes like another 6 minutes to explain the general idea and make some wishes, and then at the 12m mark he defines macros and makes the wish real in just 2m (with some enhancements later).
incanus77 1 days ago [-]
I've been trying to learn Scheme lately by way of schemesh[1], which strikes me as a very clever integration of Scheme into a shell. My favorite parts are that you mix Scheme and shell using () or {} directly, as well as shebang right into one or the other fully as the default when needed.
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 :-).
NuclearPM 4 hours ago [-]
I’m playing with creating a language where everything is an object and a function. Calls are just method missing per object or fields/slots are just canned responses to calls.
ulbu 1 days ago [-]
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
arvyy 16 hours ago [-]
Almost all of these problems are due to scheme being so absurdly fractured, it's a real shame. It's already niche, but then on top of it instead of having 2-3 robust implementations with robust cli / messages / docs / etc, you have 20 (not a hyperbole[1]), each one receiving diluted effort compared to what it could have been.
Probably the one bit that isn't strictly fracturing's fault is stacktraces -- scheme has TCO, which is nice for alot of things but it absolutely destroys dx as it relates to stacktraces. Other languages with TCO are similarly awful in that regard, including haskell (ghc has `HasCallStack` to try help it, but it's still awful to use).
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
Pay08 16 hours ago [-]
The issue with Guile's stack traces is that they get truncated beyond I think 80 characters, and frequently leave out stack frames (usually your own functions). Geiser does have a debugger, but it does things differently based on implementation and Guile's specifically is a fancy one that I couldn't get to grips with. Not that I've tried that much, as I believe it's quite new.
gausswho 1 days ago [-]
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.
oumua_don17 16 hours ago [-]
From my experience, you become a lisper truly when your Lisp image is running for days on end, you miss the live editing power and loathe the exception handling system in other languages. It takes a while and concentrated effort to get you there.
I strongly recommend anyone interested use a Lisp as their prototyping language in their workplace. Another idea is to rewrite a complex component of the software you're working on and rewrite it in Lisp. More importantly ask the questions on Lisp forums especially about using the live editing workflow.
chuckadams 1 days ago [-]
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.
varjag 1 days ago [-]
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.
Pay08 1 days ago [-]
Two websites don't sound like insubstanial projects.
r14c 1 days ago [-]
With LLMs you can make 20 websites and still not really understand the language. For learning you really do have to type the code out yourself. That's how you build familiarity and understanding. Reading code is a good starting point, but it doesn't really gel until you start writing your own ideas down, fail, and try again until it works and makes sense. Especially if you're working in a new language with unfamiliar semantics.
neilv 1 days ago [-]
> 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.
kayo_20211030 1 days ago [-]
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.
tmtvl 1 days ago [-]
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.
veqq 17 hours ago [-]
What do you mean by "Assembly-like style"?
tmtvl 15 hours ago [-]
Prog with a bunch of labels and jumps, using the bindings like ASM registers.
pfdietz 1 days ago [-]
You might try Common Lisp instead.
matheusmoreira 1 days ago [-]
I bounced off Scheme so many times I just created my own lisp instead. At some point I just accepted that I wouldn't ever be comfortable in any language other than the one I made myself. I too have the ALGOL neurotype...
Pay08 1 days ago [-]
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.
bitwize 1 days ago [-]
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
Pay08 1 days ago [-]
That sounds like a horrible way of learning Emacs, but fair enough. I don't think you need to know elisp as a prerequisite but personally I learnt enough of it to be able to get by without Customize.
You might want to read Zed's EULA.
TFNA 23 hours ago [-]
The GP is right to be offended by what you said, because his way of learning Emacs was an extremely typical one for decades. Remember that such informative and inspirational resources like Mastering Emacs didn't exist until the 2010s. I myself got into Emacs around the turn of the millennium because I wanted a free IDE for my programming-language classes at uni, and also I wanted to use Gnus which was such a capable mail and Usenet reader. It was only over years, as I amassed various problems that I needed to solve and tasks to automate, that I began learning all the tricks of Emacs customization and then eventually Emacs Lisp itself.
anthk 15 hours ago [-]
You got the ELisp intro right in the help section since forever.
TFNA 13 hours ago [-]
Of course, but not everyone has a reason to look at it in the beginning. As I said, I only became interested in hacking Elisp once I had been using Emacs for a long while (years) and eventually ran into cases where I wanted to change default behaviour. Meanwhile, all my hacking energy was going to other languages for which I just used Emacs as the IDE with the supplied major modes.
anthk 10 hours ago [-]
Ah, when I was a teen I always wanted to read everything as I had no internet at home and often you founds manuals, gems such as great programming and Math books and whatnot.
tancop 9 hours ago [-]
EULA's are for corporate customers. as a normal person you dont need to care about breaking contracts unless its related to your job
bitwize 1 days ago [-]
Well, I'm sorry for not employing a Hackernews-approved data-driven, spaced-repetition learning technique utilizing Pomodoro, Zettelkasten, and balanced gut bacteria to optimize the time to proficiency. I was frickin' 18 when I discovered Emacs (and Linux), it was 1995, and most of us were just figuring this crap out as we went.
I do not use Zed's online services, only the editor itself which is GPLv3. If I need AI I wire in a third-party ACP provider, which is easy enough to configure.
jimbob45 1 days ago [-]
I went back and forth with ChatGPT on this a while ago. I concluded that my main problem with Lisps (and C) is that types don’t have their core functions associated with them concretely. The only way to know strcat, puts, and strlen exist is…to know that they exist. This vexed me. We lightly mapped out the idea of having traits for types in lisp and then having those traits and their associations frozen at compile time. That way, you could see which traits a given type implements and feel your way around the language more confidently.
However, like “If you give a mouse a cookie”, I realized that once I had my traits that I also wanted compile-time attributes à la Rust/C# and there’s no clean way to add those to Lisp’s syntax without seriously cluttering it up (note: yes, function-level attributes would look fine but C# allows attributes on locals, iterators, and usings which just don’t mesh with Lisp’s syntax).
I gave up. Lisp is neat but it just doesn’t fit in the future of programming that Rust and C# have shown us.
tmtvl 13 hours ago [-]
> The only way to know strcat, puts, and strlen exist is…to know that they exist.
Yes, and the only way to know that Vector and Hashtable exist is... to know that they exist.
> I also wanted compile-time attributes à la Rust/C# and there’s no clean way to add those to Lisp’s syntax without seriously cluttering it up
That's what declarations are for, a Lisp could have something like:
> Lisp is neat but it just doesn’t fit in the future of programming that Rust and C# have shown us.
C♯ and Rust are dead languages, you make a change and you have to recompile the entire program and restart it from scratch. You can't ask a running program things like 'how many users are connected at the moment?'. That isn't a future I would like to see.
gwerbin 1 days ago [-]
You might be interested in the Gauche implementation of Scheme which overall has a Python-like extended standard library, notably including a lot of functions that are generic over sequences and collections which historically in Scheme (and even moreso in Lisp) had a menagerie of special-cased functions for working with them.
evdubs 21 hours ago [-]
> types don’t have their core functions associated with them concretely.
They do in Racket? You look at the "List" documentation and you find all of the functions for lists. Likewise with hash tables, numbers, strings, sequences, files, etc.
> The only way to know strcat, puts, and strlen exist is…
... to look at the documentation on docs.racket-lang.org. The powerful search includes results not only for the standard ("batteries included") library reference, but also the standard guide and 3rd party libraries.
> once I had my traits that I also wanted compile-time attributes à la Rust/C# and there’s no clean way to add those to Lisp’s syntax
I mean, why are you not able to do (trait whatever) before (define whatever) or (for-each whatever) or (let whatever)?
> Lisp is neat but it just doesn’t fit in the future of programming that Rust and C#
Have you tried it or did you just try to figure out why you don't think you like it from an LLM?
jimbob45 16 hours ago [-]
As far as I know, Racket docs are entirely human-generated. The functions aren’t tied concretely together to types by anything. In C#, types and their member functions are automatically tied and inspectable in code. That’s what I feel is missing. Also, I don’t want to take away from the phenomenal Racket docs. They’re a real treat to work with.
evdubs 15 hours ago [-]
If you're referring to C# (and Java) being the Kingdom of Nouns where a type like ArrayList is defined and contains its methods, sure, Lisp is not exactly like that, but I feel like conventions give you a similar experience. For example, functions related to hash tables all have `hash` in their name and are either constructors or they take a `hash` argument. They are contained in their own file (hash.rkt).
Also, doesn't inheritance interfere a bit with your "functions aren't tied concretely together to types" observation? You can examine a source file for ArrayList, but if it extends List, you may not see everything you expect to see, and would just defer to the docs to help you out (or click through more source files).
I assume Racket contracts can handle your concern about relating functions and types. An IDE or static analysis tool can help you find all of the functions that operate on a `dict` type if you don't want to rely on conventions where you just seek out a dict.rkt file.
For what it's worth, coming from lots of Java experience, as a Racket novice, I poked around the Racket internals and added Candlesticks to its `plot` library [0] and removed a call to a deprecated gdk function that was causing overhead when drawing text [1]. It never felt like an insurmountable task just because methods aren't defined within types.
By finding problems with Lisp, you are finding problems with s-expressions, which, to me, are so plainly superior to XML and JSON for defining data that I wish more languages would at least consider adopting them for data definitions.
> I assume Racket contracts can handle your concern about relating functions and types
contracts are not real types enforced by the compiler/runtime. that means you need to keep them in sync manually and you dont get performance benefits from typed vm instructions. you also lose a big safety net that makes sure you always call a function that can handle the values you pass in. the whole industry has been moving towards ml style strong types for the last 10 years and its for a good reason
wellpast 1 days ago [-]
> 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.
veltas 1 days ago [-]
Is it possible you're too stupid to write scheme? Because that's where I think I am, I've also tried and failed to write it a few times.
jfengel 1 days ago [-]
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.
convolvatron 1 days ago [-]
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.
leecommamichael 1 days ago [-]
Do what works for you.
SilentM68 1 days ago [-]
I've had a similar problem. I originally started learning programming with BASIC, assembly, procedural, event-driven, languages, found OOP bloated and thus counter productive, time consuming, still do. Have tried to focus on functional languages, but for some reason none stick. Not sure if it is my brain, or haven't found the right language that will work with my head. I'm trying my luck at Odin to see if that can stick with me.
throw310822 1 days ago [-]
I've had to go through my Scheme labs when I was studying CS. Even as a hobbyist with a decade of experience in various languages and a decent intuition for coding, I couldn't get my head around it. It's quite telling that a language that is hard to read, unfit for most purposes and that even proficient coders never seem to fully grasp, was chosen as a tool to introduce beginners to coding.
evdubs 21 hours ago [-]
Assuming your comment applies to Common Lisp as well, if Scheme (and Lisp) truly was, "hard to read, unfit for most purposes and that even proficient coders never seem to fully grasp," it wouldn't be the language that introduced so many features later implemented in other languages.
> Lisp pioneered many ideas in computer science, including tree data structures, automatic storage management, dynamic typing, conditionals, higher-order functions, recursion, the self-hosting compiler, and the read–eval–print loop.
So many proficient coders were able to grasp features of Lisp, find the fitness of those features, and implement them in their own languages.
bsder 24 hours ago [-]
I'm kind of especially surprised that an OOP person bounces of Scheme. OOP and Scheme both like to atomize things into webs of a zillion little functions.
The biggest advice I would give to the author is "If you're doing general programming instead of compiler construction, get off the goddamn lists, ignore recursion and use vectors and loops like somebody civilized. Don't write a macro until you have a gun to your head." Everybody I know who "bounces off" of Scheme/Lisps does so because of the idiotic "List Pedagogy" when the expert programmers almost invariably use vectors everywhere.
However, in this day and age, the lack of strong typing is a lot more of an issue. I've been more directing people toward OCaml rather than Scheme/Lisp nowadays--especially since they added multicore. However, similar advice attends: "Ignore the functional crap and type inference. Use vectors and put type signatures on all the things, you savage. OCaml works just fine as an "imperative" language."
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 probably under-weighing this factor.
The average programmer looks at SML syntax and cannot make, pardon the expression if you will, heads or tails of it.
Indeed, I'd argue the average programmer still considers recursion an advanced topic.
But isn't Python object-oriented? I mean, it's arguably more object-oriented than Java, since unlike Java, everything in Python is an object and it supports multiple inheritance. It's totally fair to not like Java, but if you like Python, then I doubt that OOP itself is what made you dislike Java.
Bonus - Racket is an accepted language on leetcode ;-)
I usually look at the editorial after the first solve to see if I missed any tricks or other approaches, and sometimes I’ll write the second solution differently if there was something interesting that I missed.
Technical interviews are different enough from day-to-day work that I still find it valuable to practice in them.
LeetCode does have an "unofficial" API to get the problem of the day. Should work for previous days as well. This code worked last I tried [2].
[1] https://github.com/ebanner/daily-coding-challenges#leetcode
[2] https://github.com/ebanner/get-daily-leetcode-problem/blob/m...
You can view the daily problem without logging in or creating an account.
Fair warning: today's daily problem is a "hard" difficulty and it is often worth skipping those or peeking at the solution. They can take a while to solve (often at least an hour) and/or rely on unusual tricks or data structures. I haven't solved this one yet so idk exactly what its deal is.
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/
https://lfe.io/
Personally I find it easiest do design data flows: think about what we need in order to compute the result. The less I have to think about state the better. Functional patterns fall out and it feels simple and easy to me, so I do it. I'm way over the phase I thought that's somehow cooler than the OOP folks, however.
A few chapters in I belly laughed when it dawned on me that it's going to be recursion until the cows come home. The authors insistence on working through the steps is endearing and comical. Great book to come back to every once in a while.
Functional or declarative programming styles appeal to me, and I love what I've learned of Erlang but never really had a job where I could use it. Used SQL a lot and quite like it as well.
Best of all, is that the syntax grammar is so simple, that a simple CLAUDE.md + a few review agents lets me move super fast using AI. I spend most of my time anchoring on the design/plan and let AI write the implementation for most of the code.
https://zv.github.io/sicp-in-texinfo
Official docs: https://docs.racket-lang.org/rhombus/index.html
Collection of small examples: https://github.com/racket/rhombus/blob/master/demo.rhm
Anyway, making Rhombus forced a few general enhancements in the Racket ecosystem, like more support for not-s-expression languages in the editor, filters in docs for languages families, and some new "backported" libraries like Treelist that behaves closer to a list in Python.
My main concern was how to make good macros without s-expressions. There is a nice video by Matthew Flatt in RacketCon 2023. The first 6 minutes and 20 seconds are internal stuff, so skip to the 380s that I added in the link: https://www.youtube.com/watch?v=OLgEL4esYU0&t=380s He takes like another 6 minutes to explain the general idea and make some wishes, and then at the 12m mark he defines macros and makes the wish real in just 2m (with some enhancements later).
[1] https://github.com/cosmos72/schemesh
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
Probably the one bit that isn't strictly fracturing's fault is stacktraces -- scheme has TCO, which is nice for alot of things but it absolutely destroys dx as it relates to stacktraces. Other languages with TCO are similarly awful in that regard, including haskell (ghc has `HasCallStack` to try help it, but it's still awful to use).
[1] https://get.scheme.org/
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 strongly recommend anyone interested use a Lisp as their prototyping language in their workplace. Another idea is to rewrite a complex component of the software you're working on and rewrite it in Lisp. More importantly ask the questions on Lisp forums especially about using the live editing workflow.
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
You might want to read Zed's EULA.
I do not use Zed's online services, only the editor itself which is GPLv3. If I need AI I wire in a third-party ACP provider, which is easy enough to configure.
However, like “If you give a mouse a cookie”, I realized that once I had my traits that I also wanted compile-time attributes à la Rust/C# and there’s no clean way to add those to Lisp’s syntax without seriously cluttering it up (note: yes, function-level attributes would look fine but C# allows attributes on locals, iterators, and usings which just don’t mesh with Lisp’s syntax).
I gave up. Lisp is neat but it just doesn’t fit in the future of programming that Rust and C# have shown us.
Yes, and the only way to know that Vector and Hashtable exist is... to know that they exist.
> I also wanted compile-time attributes à la Rust/C# and there’s no clean way to add those to Lisp’s syntax without seriously cluttering it up
That's what declarations are for, a Lisp could have something like:
Or something like: > Lisp is neat but it just doesn’t fit in the future of programming that Rust and C# have shown us.C♯ and Rust are dead languages, you make a change and you have to recompile the entire program and restart it from scratch. You can't ask a running program things like 'how many users are connected at the moment?'. That isn't a future I would like to see.
They do in Racket? You look at the "List" documentation and you find all of the functions for lists. Likewise with hash tables, numbers, strings, sequences, files, etc.
> The only way to know strcat, puts, and strlen exist is…
... to look at the documentation on docs.racket-lang.org. The powerful search includes results not only for the standard ("batteries included") library reference, but also the standard guide and 3rd party libraries.
> once I had my traits that I also wanted compile-time attributes à la Rust/C# and there’s no clean way to add those to Lisp’s syntax
I mean, why are you not able to do (trait whatever) before (define whatever) or (for-each whatever) or (let whatever)?
> Lisp is neat but it just doesn’t fit in the future of programming that Rust and C#
Have you tried it or did you just try to figure out why you don't think you like it from an LLM?
Also, doesn't inheritance interfere a bit with your "functions aren't tied concretely together to types" observation? You can examine a source file for ArrayList, but if it extends List, you may not see everything you expect to see, and would just defer to the docs to help you out (or click through more source files).
I assume Racket contracts can handle your concern about relating functions and types. An IDE or static analysis tool can help you find all of the functions that operate on a `dict` type if you don't want to rely on conventions where you just seek out a dict.rkt file.
For what it's worth, coming from lots of Java experience, as a Racket novice, I poked around the Racket internals and added Candlesticks to its `plot` library [0] and removed a call to a deprecated gdk function that was causing overhead when drawing text [1]. It never felt like an insurmountable task just because methods aren't defined within types.
By finding problems with Lisp, you are finding problems with s-expressions, which, to me, are so plainly superior to XML and JSON for defining data that I wish more languages would at least consider adopting them for data definitions.
[0] https://github.com/racket/plot/commit/7f38feaf6e28a1decec93d...
[1] https://github.com/racket/gui/pull/95
contracts are not real types enforced by the compiler/runtime. that means you need to keep them in sync manually and you dont get performance benefits from typed vm instructions. you also lose a big safety net that makes sure you always call a function that can handle the values you pass in. the whole industry has been moving towards ml style strong types for the last 10 years and its for a good reason
You’re almost there. Just stop thinking about the sequence of instructions. Focus on the information half (the values) that you need to produce.
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.
> Lisp pioneered many ideas in computer science, including tree data structures, automatic storage management, dynamic typing, conditionals, higher-order functions, recursion, the self-hosting compiler, and the read–eval–print loop.
So many proficient coders were able to grasp features of Lisp, find the fitness of those features, and implement them in their own languages.
The biggest advice I would give to the author is "If you're doing general programming instead of compiler construction, get off the goddamn lists, ignore recursion and use vectors and loops like somebody civilized. Don't write a macro until you have a gun to your head." Everybody I know who "bounces off" of Scheme/Lisps does so because of the idiotic "List Pedagogy" when the expert programmers almost invariably use vectors everywhere.
However, in this day and age, the lack of strong typing is a lot more of an issue. I've been more directing people toward OCaml rather than Scheme/Lisp nowadays--especially since they added multicore. However, similar advice attends: "Ignore the functional crap and type inference. Use vectors and put type signatures on all the things, you savage. OCaml works just fine as an "imperative" language."