Slap: Functional Concatenative Language... with a Borrow Checker?

87 points by surprisetalk


sebastien

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 :)

slightknack

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!

jonathannen

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.

munksgaard

No mention of ADTs/sum types? I see there's a match keyword, but no explanation of how it is typed?

peter-leonov

No secret allocations!

Yes, please!

azhenley

This is intriguing. Kitten had my interest for concatenative languages a decade ago: https://github.com/evincarofautumn/kitten

jnb

I’m a bit surprised that the implementation is only 2k lines despite the repo having a claude.md file.