Anonymous Recursive Functions in Racket
Posted4 months agoActive4 months ago
github.comTechstory
calmpositive
Debate
40/100
Functional ProgrammingRacketScheme
Key topics
Functional Programming
Racket
Scheme
The post discusses anonymous recursive functions in Racket, sparking a discussion on functional programming concepts and their implementation in various languages.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
3d
Peak period
22
72-84h
Avg / period
9
Comment distribution63 data points
Loading chart...
Based on 63 loaded comments
Key moments
- 01Story posted
Sep 3, 2025 at 8:39 PM EDT
4 months ago
Step 01 - 02First comment
Sep 6, 2025 at 5:07 PM EDT
3d after posting
Step 02 - 03Peak activity
22 comments in 72-84h
Hottest window of the conversation
Step 03 - 04Latest activity
Sep 9, 2025 at 11:57 PM EDT
4 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
ID: 45122069Type: storyLast synced: 11/20/2025, 3:56:10 PM
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
The joke can go on forever...
For added fun, the day he teaches it in class, he wears a t-shirt from Y-combinator the startup accelerator (and explains what its name means).
Now that we've gotten that out of the way, it remains unclear what is surprising or unpleasantly surprising about the code.
https://docs.racket-lang.org/trace/
But also, if you're processing non-linear data, you're going to want to do with a recursive function anyway. E.g., when dealing with a tree. Code below; can't seem to get multi-line code-formatting so it looks hideous:
#lang racket
(require "anon-rec.rkt") (require rackunit)
(struct mt ()) (struct node (v l r))
(define sum-tree (lam/anon (t) (cond [(mt? t) 0] [(node? t) (+ (node-v t) ($MyInvocation (node-l t)) ($MyInvocation (node-r t)))])))
(define t (node 5 (node 3 (mt) (mt)) (node 7 (node 9 (mt) (mt)) (mt))))
(check-equal? (sum-tree t) 24)
More important is the debugability. If you have a normal data structure you can see the full stack of values. If you use recursion you have to unwind through multiple call frames and look at each one individually.
Recursion is for people who want to show a neat clever trick, it isn't the best way to program.
Recursive DFS took 4 ms Iterative DFS took 8 ms
I'm sure you could optimize the explicit stack based one a bit more to reach parity with a significantly more complex program.
But might as well let ~75 years of hardware, OS, and compiler advancements do that for you when possible.
> Why would copying a single value be slower than pushing an entire call frame to the stack
Because that's not what happens. The stack arithmetic is handled in hardware increasing IPC significantly, and the 'frame' you are talking about it almost the same same size as a single value in the happy path when all the relevant optimizations work out.
> More important is the debugability
Debugging recursive programs is pretty neat with most debuggers. No, you don't unwind through anything manually, just generate a backtrace.
https://en.cppreference.com/w/cpp/container/deque.html
The stack arithmetic is handled in hardware increasing IPC significantly, and the 'frame' you are talking about it almost the same same size as a single value in the happy path when all the relevant optimizations work out.
The program stack isn't magically special, it isn't going to beat writing a single value to memory, especially if that memory is some constant sized array already on the stack.
Debugging recursive programs is pretty neat with most debuggers. No, you don't unwind through anything manually, just generate a backtrace.
No matter what kind of debugger it is you're still going to be looking at a lot of information that contains the values you're looking for instead of just looking at the values directly in an array.
Recursion gets used because it's quick, dirty and clever, not because it's the best way to do it.
I know it's doable, because I have done it.
You don't seem to understand yet how complex it will be. My guess is ~10x the number of lines of code. It'll be significantly less readable, let alone debuggable.
(btw changing from stack to vector and reserving memory outright for the allocations has virtually no change in performance.)
> The program stack isn't magically special
This is what you're missing. Yes, it is magical because the hardware optimizes for that path. That's why it's faster than what you'd think from first principles.
> it isn't going to beat writing a single value to memory
If you examine the kernel trace from this, you'll find that it has the exact same memory usage and bandwidth (and about twice the ipc). Magical, yes.
You're trying to say that pushing a function call frame is faster than writing a single value to memory and incrementing an index, and when asked to prove it you made something that allocates and deallocates chunks of memory to a linked list where every access takes multiple pointer dereferences.
I checked this with my R6RS implementation and it works just as you would expect (https://github.com/maplant/scheme-rs)
[0] https://github.com/mattwparas/steel
That being said, Steel is excellent and I highly recommend it if you just need R5RS with syntax transformers
(scroll down, after the concept is explained using Clojure)
A bit crazier, in Go with generics: https://eli.thegreenplace.net/2022/the-y-combinator-in-go-wi...
Is Racket a good language to pick up and re-learn my concepts + implement some tools? Or are there some other languages that would be better to both brush up and learn the syntax of, I do not want to fight the syntax but rather express functions as seamlessly as I can.
https://cs.brown.edu/~sk/Publications/Papers/Published/fffkb...
That might help you decide whether Racket will help you with what you're trying to brush up on.
I did give your document a read and my (naive) understanding is you basically create DSLs for each sub-part of the problem you're trying to solve?
>A LOP-based software system consists of multiple, cooperating components, each written in domain-specific languages.
and
>cooperating multi-lingual components must respect the invariants that each participating language establishes.
So basically you're enforcing rules/checks at the language level rather than compile time?
How would you recommend a complete novice attain this sort of state of mind/thought process while working in this language? Because my thoughts go simply to creating types and enforcing type-checking coupled with pure functions to avoid successful-fail at runtime programs.
Also how would one navigate the complexity of multiple abstractions while debugging?
The paper also mentions a web-server language (footnote 27), if I use racket will I be productive "out of the box" or is the recommended path to take is writing a web server language first.
Thank you again for taking the time to respond, and please do forgive me for these naive questions.
Yes, what you're describing is the "extreme" version of LOP. Of course you don't have to do it that aggressively to get working code.
Two references I like to point to:
https://www.hashcollision.org/brainfudge/
https://beautifulracket.com/
They will give you a sense of how one uses LOP productively.
You do not need to write a "web server language"! To the contrary, the Web server provides several languages to give you a trade-off between ease and power in writing server-side Web applications. So you can just write regular Racket code and serve it through the server. The server also comes with some really neat, powerful primitives (orthogonal to LOP) — like `send/suspend` — that make it much easier to write server-based code.
Even if I don't go fully into it as a production language, hopefully it'll open some avenues of thought that I do not yet possess.
Thank you for taking the time to respond, have a great day!
It focuses exclusively on FP and does not deviate from it.
¹ https://leanpub.com/fp-made-easier/
Thanks again!
Then you need to retain the personnel who give you that capability. Because they are rare, in a field in which 99%+ of developers only glue together NPM or PyPI packages. (And many use Web search or, now, Claude Code to do the glue part.)
If I founded a startup doing mostly Web-like server backend work, I'd consider doing it in Racket or another Scheme, and then using that as a carrot to be able to hire some of the most capable programmers. (And not having to bother with resume spamming noise from hardly any of the 99%+ developers, who will be pounding the most popular resume tech stack keywords instead, because their primary/sole goal is employability.)
They're niche because they're doing weird, interesting things. Like creating their own VMs to support funky features. So nobody wants to depend on them: low bus-factor.
They can do weird, interesting things because they don't have a large user-base that will yell at them about how they're breaking prod.
(Just me suggesting other alternatives right now)
https://github.com/shriram/anonymous-recursive-function/comm...
Recur has zero inconvenience. It's four letters, it verifies that you are in a tail position, and it's portable if you take code to a new function or rename a function. What's not to love?
Moreover, you can design cooperating macros that induce and take advantage of tail-position calls.
Here's a simple example that motivates tail-calls that are not tail-recursive:
https://cs.brown.edu/~sk/Publications/Papers/Published/sk-au...
recur: https://clojuredocs.org/clojure.core/recur
the recursion point to the values of the exprs. trampoline: https://clojuredocs.org/clojure.core/trampoline i.e. these emulate TCO, with similar stack consumption properties (they don't implement real TCO).(edit: formatting)
https://dl.acm.org/doi/pdf/10.1145/317636.317779
Usually the trampoline is implemented automatically by the language rather than forcing the author to confront it, though I can see why Clojure might have chosen to put the burden on the user.
https://clojure.org/about/history
https://news.ycombinator.com/item?id=45154253
would therefore not work.
2. The README literally says "Don't Use This Macro!" and references `rec` to use instead:
https://github.com/shriram/anonymous-recursive-function?tab=...