Slap: Functional Concatenative Language... with a Borrow Checker?
87 points by surprisetalk
87 points by surprisetalk
Very nice, it's good to see a fresh take on concatenative languages and see how today's expectations (performance, safety, expressiveness) can be met. Nice also that the implementation is small, it's very in line with the philosophy of this family of languages. Thanks for sharing :)
This is really cool! I can see the inspiration from APL, K, etc -- any plans for multidimensional arrays? Could be fun in combination with the SDL bindings (draw cool things with little code).
I also like how you use 'system effects' to interact with the runtime; this approach is neat! Sometimes I wonder whether if, instead of linear types to represent resources, we should use effects instead. E.g. in Koka:
fun main() {
with open("data.txt", Read)
val line = read-line()
println("Got line: " ++ line)
}
In my old language Passerine, I also planned on using algebraic effects for system inferfaces -- I think I have an old blog post or something about it... hmm. Here's a github issue that mentions it in passing:
The [type system] should support Algebraic Effects. Interaction with the FFI [which includes the runtime] should be done through such handlers for effects.
I think an older version of an experimental language Pen is where I first came across the idea? Unclear.
For languages with a runtime I admit handing effects up the stack and reifying them at the runtime level if they are not handled is a very satisfying way to implement an FFI for a scripting language.
Returning to effects for references, I think I recall vaguely this proposal to add rank-2 polymorphism to koka in a way compatible with the effect system (perhaps it was this paper), which allowed you to e.g. define a memory effect. Adapting the syntax used in the paper, something like:
scoped effect mem<s::S> {
ctl malloc(size : int) : ptr<s>
ctl free(p : ptr<s>) : ()
}
named effect ptr<s::S> in mem<s> {
fun load(offset : int) : int
fun store(offset : int, value : int) : ()
}
(Note that here s is the heap, and not the type of the pointer reference.) This allows you to make some memory, and free the pointer only if the original effect is in scope!
[[1 2 3]] [[4 5 6]] plus.Oh you did Passerine? Lemme tell you that it was absolutely beautiful. Just beautiful. Keep up the good work.
I did use FORTH quite a bit back in my uni days, so it's always nice to see a concatenative language in the wild -- baseline this is pretty cool.
I do think the "borrow" comparison is a bit stretched. I can see where you’re coming from, but trying to map this onto a Rust lens felt more distracting than clarifying.
No mention of ADTs/sum types? I see there's a match keyword, but no explanation of how it is typed?
The match keyword only works on symbols, which can’t hold payloads. I’m hoping to add a sum type with payloads in the next few weeks :) and then I can return Result monads instead of panicking everywhere haha
No secret allocations!
Yes, please!
This is intriguing. Kitten had my interest for concatenative languages a decade ago: https://github.com/evincarofautumn/kitten
I’m a bit surprised that the implementation is only 2k lines despite the repo having a claude.md file.
Given the length of the Claude file, it seems like the author is using the tool lightly. In sloppy repos the agent markdown files tend to be really long, half-defined specs. Claude is only credited on a few commits, and since the blog post doesn't have that AI smell, I get the sense the author understands it pretty well. That that said, I would love to know the reason for those strange commit messages...
I have no clue how other people use Claude, but I tend to write extremely detailed design docs (plus essays) and then have Claude generate prototypes so that I can get a feel for how all the parts fit together. Each time I find a flaw in the design (not implementation!), I throw away everything and head back to the design doc. At some point, when the design felt good enough, I asked Claude to produce ~5000 lines of working slop, and then I transformed that slop into ~2000 lines of Slap :)
For commits on small projects, I just do git commit -am "✨" ; git push whenever I reach milestones. I'll start writing real commit messages when it's a real project that people care about haha
I don't want to and won't ever personally develop this way, but I want to say this is a neat way to develop nonetheless! Sounds like a lot of fun.