Play: Statically typed Forth, compiled to WASM(play-lang.dev)
play-lang.dev
Play: Statically typed Forth, compiled to WASM
https://www.play-lang.dev
47 comments
I see your point, but in my experience way more people have heard of Forth than "concatinative language", and of those that have heard of Forth most associate it with stack oriented programming, which is at the heart of Play.
Forth also did serve as the true inspiration for Play, so it makes sense to me to put that front and center.
Forth also did serve as the true inspiration for Play, so it makes sense to me to put that front and center.
Perhaps you can elaborate, then: With the exception of a whitespace-delimited tokenization rule for the syntax and an implicit parameter stack for composing words, what else about Play would you say is inspired by Forth?
To me, that is Forth.
Something like "Forth-inspired concatenative language" might be clearer.
I'm the creator of the language, AMA.
Does it compile to WASM inside the browser, without communicating with any server?
Yes.
Congrats, this looks very nice. I played a bit around and headed then to Wikipedia's entry of Forth to learn more about the og language. Please elaborate more about why you did this project and decide for Forth from your perspective.
I realised that the code I found most readable were always in a pipeline, regardless of language (java: `one().two().three()`, lisp: `(-> one two three)`, Elm: `one |> two |> three`, etc) and I wondered what it would be like to program in a language were pipelines were not only easy, but required.
Forth-based languages was the closest I could come to that experience, but most of those languages did not have all the niceties I've come to enjoy, like strong static typing and purity.
I also wanted a language which would work equally well on the backend as the frontend, and with wasm this seemed possible.
So those were the initial ideas behind the project.
Forth-based languages was the closest I could come to that experience, but most of those languages did not have all the niceties I've come to enjoy, like strong static typing and purity.
I also wanted a language which would work equally well on the backend as the frontend, and with wasm this seemed possible.
So those were the initial ideas behind the project.
> I realised that the code I found most readable were always in a pipeline, regardless of language
I'm in a similar boat, I find the pipeline-style code to be the most pleasant and use ->, ->>, cond->, some-> very heavily in my Clojure code and I actually spent a few months with Factor before I got into Clojure.
For large chunks of my code, I find the concatenative style an excellent fit, but the remaining parts where I have to juggle the stack ruins the experience for me. I really don't want to drop, dup, rot or whatnot.
I've always wondered if there wasn't some way of making a concatenative language that naturally didn't require stack operations like that, but I haven't tried too hard to figure it out (or check if anyone else has). I guess something that might work is to combine concatenative "statements" with something like let bindings and destructuring, so you can destructure your stack to extract the stuff you need, binding them to names to be used elsewhere.
I'm in a similar boat, I find the pipeline-style code to be the most pleasant and use ->, ->>, cond->, some-> very heavily in my Clojure code and I actually spent a few months with Factor before I got into Clojure.
For large chunks of my code, I find the concatenative style an excellent fit, but the remaining parts where I have to juggle the stack ruins the experience for me. I really don't want to drop, dup, rot or whatnot.
I've always wondered if there wasn't some way of making a concatenative language that naturally didn't require stack operations like that, but I haven't tried too hard to figure it out (or check if anyone else has). I guess something that might work is to combine concatenative "statements" with something like let bindings and destructuring, so you can destructure your stack to extract the stuff you need, binding them to names to be used elsewhere.
Does it compile to threaded forth or does it compile to a blob of lowest level instructions or what?
It compiles, ahead of time, to low level web assembly.
Do you plan to do threaded forth? It does wonders for runtime word redefinition, which I would look for for a niche project of mine involving some kind of ultra portable cross-platform Forth (WASM is perfect) in a sandboxed environment (because multi-player, WASM perfect as well!)
Having implemented a threaded Forth with WASM, the most annoying problem is that WASM doesn't support jumps to arbitrary memory locations. You can either call functions (and you don't have access to the call stack), jump backward to a previously defined block entry point or jump forward to the end of a previously defined block. Threading then requires a double indirection in the core loop, and you have to use an explicit IP "register" in a global variable.
I don't plan to. I'm sure word redefinition is nice in certain cases, but it's not something I'm looking for in a pure and immutable language.
Looks great!
I have a question the planned Alpha 7 feature: "smaller than 32-bit values (char/byte)" - I'm so used to bytes being a fundamental primitive in programming languages that it's hard for me to imagine not immediately having access to them.
Can you explain a bit why this is non-trivial, and what the implementation is going to look like?
I have a question the planned Alpha 7 feature: "smaller than 32-bit values (char/byte)" - I'm so used to bytes being a fundamental primitive in programming languages that it's hard for me to imagine not immediately having access to them.
Can you explain a bit why this is non-trivial, and what the implementation is going to look like?
> I'm so used to bytes being a fundamental primitive in programming languages
Sorry to hijack the question, but tangentially, this actually used to be very unusual.The B language, C's predecessor, did not have a char data type. Or int, either. Only a single type -- a 16 bit word then.
At the time, most computers used word addressing exclusively, so you couldn't directly access bytes, only words.
This is also why some early computers could easily be e.g. 36 bit machines, like the PDP-10.
C was, thus, an extension of B(CPL) to allow both words and bytes, or "char"s, using the PDP-11's brand new byte addressing. (among other additions like structs)
I love historical facts like these. Thank you for sharing!
It’s more that bytes aren’t necessary for the first few releases, and it will probably not be all that exciting to implement, so I’d rather focus on other things. Same with arrays.
looks nice. why did you pick fossil (I like the idea) and how are you finding working with it so far (e.g. comparison to github)?
Like you I just really like the idea of it. It's self-hosting, and it keeps all tickets, wiki and forum posts inside the repo, so that I can work on things offline and do backups very easily.
Fossil doesn't support basic code review and merge requests though (like Github PRs). Actually it kinda does through bundles, but Github would probably be better for that use case. Don't think it will be a problem any time soon for this project, though.
All in all, I've used Fossil for about a year and I'm pretty happy with it.
Fossil doesn't support basic code review and merge requests though (like Github PRs). Actually it kinda does through bundles, but Github would probably be better for that use case. Don't think it will be a problem any time soon for this project, though.
All in all, I've used Fossil for about a year and I'm pretty happy with it.
What do you make of the two other statically typed Forth-like languages, StrongForth and Kitten? Did you use them as a reference for the project?
Links:
* (StrongForth) http://www.arestlessmind.org/2009/02/03/intro.html
* (Kitten) https://kittenlang.org/
Links:
* (StrongForth) http://www.arestlessmind.org/2009/02/03/intro.html
* (Kitten) https://kittenlang.org/
Didn't know about StrongForth.
As for Kitten, I didn't like the syntax. To me the interesting thing about Forth-like languages is the postfix/rpn syntax, which Kitten moves to far away from. I really liked how `if` in Factor was just a postfix word like any other, where the then- and else-clause was just quotation arguments.
I'm sure Kitten is a fine language, it just wasn't what I was looking for.
As for Kitten, I didn't like the syntax. To me the interesting thing about Forth-like languages is the postfix/rpn syntax, which Kitten moves to far away from. I really liked how `if` in Factor was just a postfix word like any other, where the then- and else-clause was just quotation arguments.
I'm sure Kitten is a fine language, it just wasn't what I was looking for.
Replies like this always make me cringe in self doubt.
There are so many projects I started because I was interested in the problem set, got to a point where I was proud of the result, only to change my search terms just a little and find an established project by someone else.
And sometimes I read replies like this and I think it feels like someone just saying "repost: " and linking fifteen other HN threads.
There are so many projects I started because I was interested in the problem set, got to a point where I was proud of the result, only to change my search terms just a little and find an established project by someone else.
And sometimes I read replies like this and I think it feels like someone just saying "repost: " and linking fifteen other HN threads.
Wasn't my intent to dismiss it as anything like a repost, the WebAssembly target is all new, Kitten and StrongForth don't offer that.
If it's a problem domain that needs more exploration, there's nothing wrong with forging ahead. If nobody ever did that we'd still be using COBOL.
Also Mirth (which is not established but looks fun).
Not that it matters, but there is an established web framework called play fyi: https://www.playframework.com
Can you tell me a bit about your background and your journey towards writing WASM, and learning to write a complier? (for context I never took a compiler course, vaguely know what lex and yacc are)
Not OP, but I found WASM to be a very gentle target for my experiments.
It is a rather restricted and "high level" language and has a nice, human readable, s-expression text format (WAT). For example it is perfectly reasonable to write WAT by hand after learning the basics.
Also there is plenty of fantastic, modern documentation, tooling and specification around WASM.
It is a rather restricted and "high level" language and has a nice, human readable, s-expression text format (WAT). For example it is perfectly reasonable to write WAT by hand after learning the basics.
Also there is plenty of fantastic, modern documentation, tooling and specification around WASM.
thanks! Is there a favourite resource you've come across?
Self-thought developer. Java, kotlin and Elm (fullstack) is my day job. Core contributor to Elm.
If you want to create a language that can run in browsers, you either need to target js or wasm. Wasm seemed like the better fit (i’m done with 53-bit ints), so I googled everything on that.
Read a book on GC (wasm has no such thing built in), read two books on Forth (after being intruiged by its syntax), Learned about compilers by looking at Elm source code.
I guess I’ve been around so many languages that it’s impossible not to form an oppi ion on what makes a good language, so once I knew the basics it was difficult to avoid making my own.
If you want to create a language that can run in browsers, you either need to target js or wasm. Wasm seemed like the better fit (i’m done with 53-bit ints), so I googled everything on that.
Read a book on GC (wasm has no such thing built in), read two books on Forth (after being intruiged by its syntax), Learned about compilers by looking at Elm source code.
I guess I’ve been around so many languages that it’s impossible not to form an oppi ion on what makes a good language, so once I knew the basics it was difficult to avoid making my own.
> The compiler of Play is written in Elm.
Ooo la la! How was that?
Ooo la la! How was that?
Quite pleasent :)
Compilers are really just pure functions of String -> String/Bytes, so a pure functional language is a pretty good fit.
Besides, I know Elm like the back of my hand. I’d probably write in Elm even if it was a poor fit.
Compilers are really just pure functions of String -> String/Bytes, so a pure functional language is a pretty good fit.
Besides, I know Elm like the back of my hand. I’d probably write in Elm even if it was a poor fit.
Very interesting, thanks.
I'm working with Joy using Prolog and it's dreamy. I just started to work on a compiler (until I got sidetracked) so I'm going to read your code with interest. (I thought about WASM as a target but I'm not trying to make a real language.)
I'm working with Joy using Prolog and it's dreamy. I just started to work on a compiler (until I got sidetracked) so I'm going to read your code with interest. (I thought about WASM as a target but I'm not trying to make a real language.)
Lobste.rs thread: https://lobste.rs/s/24gfue/play_statically_typed_forth_compi...
I want to like WASM but even simple things like continuations, tail call elimination, and dereferencing function pointers essentially require building your own VM ontop of it. Why couldn't we just agree on some sane, typeless bytecode and figure out how to write compilers to target it?
Are there any holes in the type system and how does it behave when types don't unify?
I.e., if I have user input that might put different things on the stack depending on the input.
I.e., if I have user input that might put different things on the stack depending on the input.
Depends, are you talking about holes as in a type annotation that let’s the compiler help you figure out the type, or holes as in bugs or missing functionality?
In the first case, no, but I see the utility and might add it in later.
In the second case: probably. This is Alpha 1, after all,
In the example you mention, Play would infer that the code places a Union on the stack, and would require a multi-word (pattern matching word) to deal with it later.
In the first case, no, but I see the utility and might add it in later.
In the second case: probably. This is Alpha 1, after all,
In the example you mention, Play would infer that the code places a Union on the stack, and would require a multi-word (pattern matching word) to deal with it later.
I wish every post about a programming language included "language" in its title. Perhaps we should establish such a convention. It would be cool to bring up all the posts about the emerging/experimental programming languages.
I did consider it, but decided against it because I thought «statically typed» and «Forth» made it obvious, and I’m not a fan of long titles.
Perhaps you are right. "statically typed" (as well as other programming language attributes) can be taken for another search request keyword to find programming languages.
The advantage of it being in the title is for searching.
I agree in this case, because it should be "Forth-like language" rather than just "Forth"
In Forth a compiled word definition begins with : and ends in ; while Play definitions begin at :def, use : to separate signatures from word bodies, and appear to be implicitly terminated wherever the next definition begins. Forth definitions always appear in "reading order"- words are declared before they are used- but the very first example on the Play home page inverts this convention. It feels rather like reading pig-latin, or perhaps ML wearing a Forth-skin-suit.
Forth's hybrid compiled/interpreted nature is at the heart of its expressiveness; so far as I can tell, this is entirely absent from Play. How would I define a word like "when:", "def:", or "["?
In my opinion it would be clearer and more accurate to describe Play as simply a concatenative functional language.