Concatenative Language
concatenative.org
concatenative.org
Some points:
- Working with Joy has convinced me that syntax is a MacGuffin. You have to have some syntax to "move the story along" but it's not important in and of itself. (The Maltese Falcon.) All this work on languages and parsing is fun and useful, but now it seems to me like a bit of a sideshow.
- It's relatively easy to manipulate Joy expressions in a kind of mathematical way. This is the "missing link" of Functional Programming: Do math to derive programs like Backus said.
- Related to the above, Joy is sooooooooo simple. It is simpler and more elegant that Forth or even Lisp.
- The Prolog interpreter is also a type inferencer! It can interpret over abstract stacks.
IMO, it's one of those languages like Prolog or APL that you should learn even if you never use it, just to expand your mind.
[A] a = A
[A] b = [[A]]
[A] [B] c = [A B]
[A] d = [A] [A]
[A] e =
[A] [B] f = [B] [A] S x y z = x z (y z)
K x y = x
The following by itself also suffices, but needs K in its definition: S' x y z = x z (y (K z))http://tunes.org/~iepos/joy.html#conssipk
This should be expected, because concatenative is a syntax style alternative to applicative syntax for functions, and has no trouble with lambda calculus semantics.
The main difference (why Joy uses (s',k) instead of (s,k)) is that Joy has quoting, but this just for convenience for large data, not essential.
Do you know of any concatenative language without quoting?
I find combinary languages simpler than concatenative ones, since the former uses only application as a composition mechanism, whereas the latter uses both concatenation and grouping with [].
[1] https://math.stackexchange.com/questions/839926/is-there-a-p...
How is concatenative grouping/quoting different from combinatory parentheses (or your binary equivalent).
In pure lambda calculus you need parentheses to disambiguate, because everything is a function and there are no natural non-functional values. In a concatenative language you need something to solve that problem, maybe quoting does that?
I'm over my head when I think about precisely how "small" these languages are, when trying to be explicit about how much is hidden in the virtual machine of evaluating a program (string of symbols).
* EDIT: Not merely expanding a base but e.g. comparing a 6-set with a /different/ 2-set, as above.
https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.19....
Y'=SSK(S(K(SS(S(SSK))))K)=X(XX)X(XX)XX(X(XX)(XX(X(XX)X(XX)(X(XX)(X(XX)X(XX)XX))))XX)
X(XX)X(XX)XX [ 0, 1 ] == {(, X}] Binary Combinatory Logic Truth: 1101111The majority of programmers seem to prefer procedural.
Anyways, the majority of programmers like general purpose languages that are popular with other programmers, because that's how you get employed and make money. And what has happened in the last 20 years is that techniques from functional & vector languages have made their way into mainstream languages. Either in the syntax or in their libraries.
That's a good thing after the previous decade+ of "object orientation will solve all our problems" dogma.
Concatenative approaches remain relatively under-explored though.
There's also the middle layer trick (ala middle ground DSLs).
But the goal is the same, ability to converge quickly on a proper solution. Something I rarely see talked about (but my radar has a short scope)
The wildest one I've seen was one that allowed both Imperative and Declarative programming in a semi-sane, yet mixed manner, it was called Metamine. I have a clone of the source, but I want to get stoical working well before I dig into that one.
%!PS % -John Tromp http://tromp.github.io/
/t{dup 1 sub gsave dup 0 gt{[.4 .2 -.2 .4 .4 .2]concat t currentgray
.8 mul .2 add setgray -1 1 scale t -1 2 translate t 1 -1 scale t[0 1
1 0 0 2]concat t pop}{0 moveto 1 0 lineto 0 2 lineto closepath clip
fill}ifelse grestore}def 10 10 translate 600 600 scale 5 t showpage
for producing a pinwheel tiling [1]."Traditional" Verb-Object order is tricky because you have to save the whole stack of functions before you can start reducing anything. But it's nice if you have partial/lazy evaluation and might discard argument/function calls without evaluating them.
For human UI, it's trivial to view/edit a program displayed in reverse, and add some redundant parentheses, for an applicative syntax, if you like.
Like Forth, the "killer app" of this RPN syntax is the ability to factor and refactor your code, along with the simple stack-based execution model this allows the code to pretty closely reflect the essential mental model of the problem you're trying to solve. There is a very precise mathematical model for this in Category Theory but I'm not a good enough mathematician to attempt to elucidate it. If you're "doing it right" your code is evolving towards something like the Kolmogorov complexity of the domain/problem. Chuck Moore pointed out that Forth programs can often be smaller than the equivalent program written in ASM.
Anyway, the result is that you have a lot of small definitions, but each one captures a single coherent thought about your program, so it's easy to read. Plus you get used to the RPN and stack themselves, which makes it easier.
Any pointers to where this is elaborated on more precisely?
Specifically, I'm skeptical about the universality of the claim, Forth allows you to approximate the essential complexity of _any_ problem ? I'm not an enemy of Forth but every language must surely make some things awkward right?
No, not off the top of my head. There is "Thinking Forth" by Leo Brodie (it's a whole book but worth the read. You can get official free PDFs here: http://thinking-forth.sourceforge.net/ )
It's a natural consequence from the ease of refactoring. Boilerplate and repetitious stuff gets refactored, leaving just the actual gnarly bits to take up most of the LoC.
> every language must surely make some things awkward right?
I think there's some theorem to that effect, no? (I want to say Rice's Theorem but that's not it.)
Forth is typically implemented in such a way as to give easy access to the underlying hardware, so in theory (and often in practice too) if there's some faculty you're missing from some other language or execution model you can implement it in Forth. People have made, e.g. object models and logic engines in Forth.
Keeping computations on the stack then feels similar to the push to keep computations out of the IO monad in Haskell.
: min 2dup < if drop else nip then ;
If you want to write programs that solve your daily problems you use normal Forth. If you want to write a PhD or play, you use the fancy stuff.So yes, from my experience, it will take more effort to process mentally; simply because you're doing more work.
$ timeout 1h notify <event> send-telegram <message>
here's we send <message> if <event> occurs within an hour (3 commands: `timeout`, `notify`, `send-telegram`).The link splits languages in to applicative vs concatenative. This is straight up Church-Turing. Where Applicative is Church's a program is function application, and Concatenative is Turing's a program is a list of instructions concatenated on an infinite tape.