Hacker News new | past | comments | ask | show | jobs | submit login
Stabel: A pure, concatenative and statically typed programming language (stabel-lang.org)
90 points by Skinney on June 16, 2021 | hide | past | favorite | 58 comments




I'm the author of Stabel. AMA.


I've never heard of a concatinative programming language before this. I can of course google, but since you obviously find these languages very interesting, I'll ask: what's a first, interesting thing I should know about them?


Forth is the grand-daddy I believe. Kitten is a new one and pretty cool. (search Youtube for a demo)

Concatenative programming is interesting in that the arguments, in and out, are implicit because they typically reside on some form of stack or as objects on a stack. (implementation details)

It might sound useless but one benefit is it simplifies factoring code greatly. If you see a common phrase in your code repeated many times, you can just cut it, paste it into a new definition, replace all the instances of the common phrase with the new definition and everything still works as before.


>If you see a common phrase in your code repeated many times, you can just cut it, paste it into a new definition, replace all the instances of the common phrase with the new definition and everything still works as before.

This is a profound distillation of the essence of Forth. You can stop repeating yourself without the need to work out all the parameters, variables, return values, etc.


The underlying limitation though is that this is only literally true of FORTH code that is literally repeating itself - identical sequences of tokens. As soon as you start pervasively using stack manipulation ops to deal with more structured kinds of data and control than what's involved in very simple programs, this property becomes rather less useful.


I take your point that it is not perfect but it is simpler than factoring where named data is required.

My experience is that when it doesn't work, it might indicate that the code is fighting with how Forth is easiest to use.

I try to use Forth per Chuck Moore's philosophy and use many small definitions. This makes the code more like functional programming such that even data is handled by code snippets. When I get this right, factoring becomes trivial.


The trade off made for not having to deal with getting the parameters named, etc... is that if you're off by 1 anywhere in the number of parameters, everything blows up, which is why Forth is hard to write big systems in.


For sure feeding wrong args to a function is never good.

Experienced Forth users seldom have that particular argument count problem but all the other mistakes in programming are on the table. There is no free lunch.

The Forth coding process involves using interactive testing of your code as you write it. Every variable, constant, data-structure and sub-routine can be easily verified at the interpreter. And... given what you said it is advisable to do it. :)

ANS Forth supports local variables for those times when "stackrobatics" might make you crazy.

Although small, the Forth community has made good progress in this alternative way to code. Some of the newer concatenitive languages working with a clean slate have gone farther in improving the programmer interface. Forth remains at its core close to the metal.

Is a 1.2 million LOC large enough? https://ribccs.com/solutions/solution-candy Written in MPE VFX Forth, native code compiler for Windows.

*I have no connection to MPE UK but have used their cross-compilers in the past.


Another concatenative programming language: https://factorcode.org/


It has several interesting properties. It is quite a rabbit hole, but a nice one to get lost into.

I would say that the most interesting thing you should know about them is that practically everything they do is manipulating a stack (actually, there are usually two stacks, I don't know if this is the case for Stabel).

What I find more interesting about them is their simplicity (comparable only to Lisp), which makes possible writing a basic interpreter in only a few lines of code. Being more concrete, an important property of concatenative languages is that they allow a point-free programming style, which gets a bit to get used to but really makes you think in a different way.

If you already feel curious, I would suggest the book "Starting FORTH" before looking into more modern (and practical) alternatives.


... that it is spelled "concatenative". The website makes the same mistake as the title here.


Thank you! I’ll fix it :)


Another minor typo, "web assembly" should be WebAssembly (no space, and CamelCase).


LOL, Burn! :D


To me, a very appealing feature is how everything becomes a pipeline. Other languages needs careful api design for objects/structs/records to be used in a pipeline, but in concatenative languages it is implicit.


This wiki about concatenative programming language probably has all the answers: https://concatenative.org/wiki/view/Concatenative%20language


Very nice! I've toyed with Joy a bit and I feel like concatenative languages are, uh, important. Stabel seems like an awesome entry in the space.

Have you seen http://conal.net/papers/compiling-to-categories/ ?

(Also s/mayor/major/ )


I had not seen that paper. Thank you for sharing with me =)


Would it be possible to implement the "recrusive combinators" like linrec, and binrec, from Joy in the language itself? I think Joy provides it as part of the implementation.


I think so. I don't have much experience with Joy, but `if` is defined as a regular function in Stabel's stdlib.


This is a paradigm I never really explored. I know of Forth, and Joy[0].

For me as a working programmer: What would you say is the main benefit of Stable, how does it relate to the above mentioned languages?

Are there specific use-cases for the language that you think are exceptional?

What is the general mindset that a Stable user has, what is important to you and what isn't?

[0] https://en.wikipedia.org/wiki/Joy_(programming_language)


Stabel is staticly typed with strict control of side effects (they're present in the type system). Since Stabel compiles to wasm, it can and will eventually be run in the browser (the playground at the Stabel website works by compiling code to wasm locally and executing it).

I'm kinda hoping that Stabel will fill the same niche as Elm, but also be able to run outside the browser so you can use the same benefits for other things than just making SPAs.

If you are a little aquinted with Elm, you can read this article about how Elm inspired Stabel: https://fossils.stabel-lang.org/compiler/wiki?name=How+Elm+i...


What's the main motivation behind creating Stable, and what philosophy or principals are guiding your decisions about how to build the language?


I originally wanted to create "Elm with unions instead of ADTs, that compiles to wasm", but I had recently discovered Factor and Forth and found I enjoyed the syntax and semantics better than ML.

I'm a minimalist, I'm not fond of languages that grow and grow and grow with features. So my goal is to create the smallest language that is still flexible enough to solve most of my day-to-day problems (I use Elm and Kotlin for work), and a language which is easy to reason about.

For more on how Elm inspired Stabel, read this: https://fossils.stabel-lang.org/compiler/wiki?name=How+Elm+i...

Note: It's Stabel, not Stable. Stabel is norwegian and means stack.


Do you have a particular use case in mind?

I think that the WebAssembly world needs more programming languages that produce short byte code to be useful in browsers (and that's why I thing Go with its stdlib is not an appropriate source language for the browser environment). Do you think that Stabel could fit that area?


The goal is that Stabel should work well in browsers, and part of that is producing small .wasm files. So yes, I think it should be a good fit.

Stabel is also aimed at backends and CLI tools.


How does this compare to the Cat language, which is also pure and statically typed?

Also, what "ring of complexity" does Stabel's type system live in, i.e. does it support rank-n polymorphism?


I've only looked at Kitten, but since that language aims to be the spiritual successor to Cat, I believe these comments still applies.

Kitten allows infix operators, from what I've seen, which IMHO reduces one of the benefits of the Forth-like syntax: factorability. It also has custom syntax for if's and matches. Stabel is more minimal in this sense.

Stabel also has unions, instead of ADTs. This means, among other things, that you don't need to wrap a `String` into `Just String`, you can just use `String` directly on any function that takes in a `Maybe String`. This let's you change how a function works without breaking call-sites (`String` -> `Maybe String` isn't necessarily breaking) and performance is better since you need less boxing. Type inference takes a hit, though. The list `[ 1, "two", 3.0 ]` is suddenly valid as the type is inferred to be a union of Int, String and Float.

rank-n polymorphism is not implemented. Might come later. Currently the focus is to get Stabel to the point where it can solve the same set of problems as Elm and once I reach that point I'll start looking at fancier things.


Or Kitten for that matter.

https://kittenlang.org/


I know how Forth works, how does Stabel relate to it?

For example, the definition of a cube function, and using it, in forth

  : cube dup dup * * ;

  3 cube .

  27


def: cube : dup dup * *

def: main : 3 cube


That looks almost identical, does Stabel offer a way to type check the parameter or anything?


Yes. Stabel is staticly typed, and types are inferred if the type signature is left out. You could also write the code as:

    def: cube
    type: Int -- Int
    : dup dup * *


I never thought about this much but now and it has been so since Forth anyway: why are definitions prefix? Why do they not gobble up the stack? I always found it easier to create everything uniform like [0]. And I did that for many years with asm and c as well because I thought it was faster than porting something (it probably was not but he).

I just like to minimize exceptions to rules and this seems a weird one to me. But probably has reasons I am unaware of (besides subjective readability which is, of course a very good reason).

[0] https://gist.github.com/tluyben/16ee2645c4c8aed813005d51488d...


Yes, in my own toy concatenative language, define is just a regular procedure. It takes two arguments off the stack. The first is a pointer to the name, the second is a pointer to a quotation with the definition body. It intentionally looks Forth-like but it's different under the hood:

    [ dup + ]      \ writes a lambda function to heap and push ptr
      : double ;   \ : writes the next symbol as a string to heap and push ptr
                   \ ; is define, writes definition into dict
The reason Forth did it the classic Forth way originally, I suspect, is because Forth's parser needs to be told to switch modes. Stop executing words directly and start compiling them. There isn't really anywhere to store a temporary string, so you need to tell it to start writing into the dictionary entry before you give it the string. In my language, using [ ] for quotations starts writing to the heap, which is also where strings go. This has the same practical effect.


I agree in principle about the benefits of everything being uniform. For Stabel, specifically, I need some way of seperating function-definition from function-implementation, and a prefix meta language seemed like the best bet. Didn't look at it too closely, though.


Thanks and, nice work with Stabel, we need more experiments in this space.


Does Stable support polymorphic functions? What kinds of data abstractions are possible? (I.e. something like records / structs / classes / Lua-like tables?)


Stabel has generics, but no protocols/interfaces/typeclasses _yet_.

Stabel allows you to define structs and unions, which gives you a lot of flexibility.


Great, now could you have a second copy of cube that did floating point?


Stabel doesn’t have floats, yet, but when it does: yes, if you put it in a different module.


So Stabel does not support (ad hoc) overloading, right? Will it support it?


Right.

Not sure. Ad-hoc polymorphism is currently in the design-phase and I'm looking at many possible solutions. But before I get to that point I still have a lot of stuff to implement first.


could you clarify what kinds of things are stored in the standard library, and how they are included, including their relevancy for particular types of programs ?


Currently, all there is are some simple stack manipulators, simple math stuff, a Pair (tuple) type, a Maybe (nullish) type and a linked List.

The stdlib will get more datastructures (Array, Dict, Set, Result, Triple...), some basic side effects (randomness, time) and functions to map to wasm intrinsics like bit-shift and math operations.

My aim is to keep the stdlib relatively small and portable between wasi and browser environments.


Resources/book/paper recommendations for understanding concatenative programming?


The articles by Manfred von Thun about the Joy language are a good start:

https://hypercubed.github.io/joy/joy.html


thanks for the ref


starting forth and thinking forth.


thanks for the ref


Implementation details: The compiler is written in Elm and compiles to WASM.


Seems like Forth on steroids. Kind of neat.


Love the name!


s/concatinative/concatenative/


I reviewed this and would like to point out that it does not follow a good practice of including the full date of an event. Stating something will happen in December without mentioning a year imposes a burden on the reader to find out if the document is relevant to the current year, or perhaps is old, requiring a extensive search to find out context.


Tech-note [9c56dcedd9] at [2021-06-14 09:57:01] entered by user robin.hansen on [2021-06-14 10:09:03]:

v0.2.0-alpha released. Module support has landed.


If you click the "Detail" tab at the top of the page, you get the date it was posted.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: