I’m hoping by the end of the year. All of the “difficult” things are finished (control flow, syntax transformers, call/cc, dynamic wind, exceptions, libraries, etc) and it’s just a matter of filling missing base library functions. If there’s something in particular that you need you’re welcome to file and issue or post a message on the discord and I’ll prioritize it.
That being said, Steel is excellent and I highly recommend it if you just need R5RS with syntax transformers
23 hours ago [-]
skrishnamurthi 21 hours ago [-]
Wow — scheme-rs is such a neat project! Hadn't heard of it before!
maplant 19 hours ago [-]
Thanks! I haven’t really publicized it, my goal is to get it finished first, but I will be presenting on it at the scheme workshop at ICFP/SPLASH
skrishnamurthi 7 hours ago [-]
Neat! Will see if I can make it (though I'll probably have to be dealing with OOPSLA stuff at the same time )-:).
adityaathalye 18 hours ago [-]
In Clojure...
((fn [xs ret]
(if (empty? xs)
ret
(recur (rest xs)
(+ ret (first xs)))))
(range 5) 0)
=> 10
nb. Clojure doesn't have automatic tail call optimisation. We need to explicitly emulate it with`recur`.
skrishnamurthi 2 hours ago [-]
It's not the same thing. `recur` in Clojure must be in tail-position. This program
I was trying to say something like that with my note in the GP comment:
> "nb. Clojure doesn't have automatic tail call optimisation. We need to explicitly emulate it with`recur`."
Just an average joe programmer here... advanced macrology is way above my pay grade :sweat-smile:.
JonChesterfield 13 hours ago [-]
Do the clojure folks still insist this is a feature, as opposed to an incomplete compiler leaking limitations into their world?
jayceedenton 12 hours ago [-]
Without the explicit recur it's far too easy to misidentify a tail call and use recursion where it's not safe.
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?
rgherdt 4 hours ago [-]
That doesn't work for mutual recursion, what is quite common in Scheme programs. Besides, tail call optimization is not only useful in recursion.
skrishnamurthi 2 hours ago [-]
Tails calls are especially useful in languages with macros. You don't know what context you are in, you just generate the call that makes sense. If the call happens to be in tail-position, you get the benefit of it.
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:
> Evaluates the exprs in order, then, in parallel, rebinds the bindings of
the recursion point to the values of the exprs.
(def factorial
(fn [n]
(loop [cnt n
acc 1]
(if (zero? cnt)
acc
(recur (dec cnt) (* acc cnt))
; in loop cnt will take the value (dec cnt)
; and acc will take the value (* acc cnt)
))))
> trampoline can be used to convert algorithms requiring mutual recursion without stack consumption.
i.e. these emulate TCO, with similar stack consumption properties (they don't implement real TCO).
(edit: formatting)
skrishnamurthi 59 minutes ago [-]
Thanks for the pointers. Trampolining is an old idea for obtaining tail-calls. It's a kind of folk-wisdom that has been rediscovered many times, as the related work here shows:
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.
h4ch1 21 hours ago [-]
Tangential, but I've been wanting to dive back into FP for quite sometime; for context I used Haskell at a payments corp ~10 years back, working mostly with Typescript, Zig and Nim for the past couple of years, realizing I am basically trying to do FP in most of these languages.
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.
skrishnamurthi 21 hours ago [-]
Racket is a rich and powerful language, but it is also designed with certain specific ideas in mind. You can learn more about the "zen" of Racket here:
That might help you decide whether Racket will help you with what you're trying to brush up on.
h4ch1 21 hours ago [-]
Thank you for the response professor, really appreciate it from one of the creators of the language itself;
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.
skrishnamurthi 21 hours ago [-]
These are great 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.
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.
h4ch1 21 hours ago [-]
Understood. Will dive deeper into Racket to get a proper understanding since it's created an itch because I still don't understand it :)
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!
humanfromearth9 12 hours ago [-]
You could try Purescript, with the book¹ written by Charles Scalfani.
It focuses exclusively on FP and does not deviate from it.
In addition to the general sibling comments, I can personally attest that Shriram knows what the Y combinator is and has been teaching students about it for at least 25 years. My own lecture notes from one of his classes about the lambda calculus and the Y combinator were for a long time on the front page of google results for info about either!
plaitowin 24 hours ago [-]
I'm pretty sure Shriram Krishnamurthi understands the Y combinator...
All the languages I like have niche ecosystems which have a lot of drawbacks
neilv 19 hours ago [-]
In such ecosystems, for long-term, evolving production work (when you don't know all your eventual needs upfront), you need to have the institutional capability to build from scratch whatever components you might need. Just in case whatever you need later doesn't yet exist in the ecosystem.
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.)
skrishnamurthi 7 hours ago [-]
The correlation is likely causal in both directions.
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.
This isn't meant to be a good programming mechanism, it's meant to be an illustration of how to use the macro system.
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:
Recursion just ends up using the call stack as a stack data structure. I would much rather use an actual stack data structure, that will be easier to debug and have better locality since there isn't an entire call frame overhead to put one value into the stack.
treyd 1 days ago [-]
Tail-recursive functions in Racket are optimized down to essentially for loops.
(Just me suggesting other alternatives right now)
https://github.com/shriram/anonymous-recursive-function/comm...
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
https://news.ycombinator.com/item?id=45154253
would therefore not work.
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.
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/
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.
The joke can go on forever...
2. The README literally says "Don't Use This Macro!" and references `rec` to use instead:
https://github.com/shriram/anonymous-recursive-function?tab=...
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.
(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...
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)
https://docs.racket-lang.org/trace/