News for hackers. Stuff that matters.
Re:Why concatenative programming matters (2012) by layer8 on Sunday July 17, 2022 @08:39AM (#32126263)
The ergonomic drawback of concatenative languages is that when you see `a b c d e`, you don’t know whether it means `e(d(c(b(a))))`, or `e(b(a), d(c))`, or `e(a, b, c, d)`, or `b(a), e(c, d)`, etc. The answer depends on the types of a, b, c, d, and e. The syntax `a b c d e` is a linearization of the call tree, whose node structure can be recovered from the types of the symbols (assuming the language is statically typed). As a human reader, it means you have to know the types to understand the structure of the expression. This imposes a cognitive load that other types of languages don’t.

It also removes redundancy that can be helpful to prevent programming errors. While the type checker verifies that the types match a structuring of the expression, that derived structure is not directly visible to the human programmer, and the programmer cannot easily see whether it actually matches the programmer’s understanding.

Re:Why concatenative programming matters (2012) by carapace on Sunday July 17, 2022 @10:34AM (#32127154)
In practice, this rarely comes up. You're almost never looking at ambiguous expressions. Instead the code is written as semantic factoring of the desired "bridge" (program) between the problem domain and solution space. (See "Starting Forth" and especially "Thinking Forth".)
Re:Why concatenative programming matters (2012) by whartung on Sunday July 17, 2022 @11:54AM (#32127886)
The other issue, specifically in Forths, is that it's not immediately obvious WHEN an argument is processed.

For example, you could have a construct such:

    ( read first block from FILE.SCR )
    OPEN FILE.SCR 1 BLOCK
But if you try to define a word to do that:

    : get-block OPEN FILE.SCR 1 BLOCK ;
You will find the second one doesn't work because OPEN is special, reading from the input stream for the filename at RUN TIME, not COMPILE time.

The OPEN command is more suited to the immediate "shell" experience, than for use in code. But it's not obvious, certainly not immediately apparent at first sight. Need to dig into the documentation is understand the difference.

Re:Why concatenative programming matters (2012) by astrobe_ on Sunday July 17, 2022 @12:52PM (#32128496)
Not the best example. The lack of quotes around file.scr is an extra-giant-huge hint that OPEN does some parsing, and therefore you should check the docs before using it in a definition (actually it is so very likely that it "won't work" in a definition that you won't even bother checking the docs).

Example aside, this type of word is usually used in a DSL-y way and with parsimony. Confusing uses are to be filed in the misuse/abuse category.

Re:Why concatenative programming matters (2012) by vitiral on Sunday July 17, 2022 @09:28AM (#32126581)
Like the author said, that is optional. I'm writing a concatenative language with a C-similar syntax

GitHub.com/civboot/fngi

Re:Why concatenative programming matters (2012) by layer8 on Sunday July 17, 2022 @09:35AM (#32126624)
It’s not optional if you want partial application/implicit composition. You can certainly have multiple syntaxes within the same language, but you can’t get the benefits of concatenative languages without also getting the drawbacks.
Re:Why concatenative programming matters (2012) by vitiral on Sunday July 17, 2022 @12:09PM (#32128029)
Interesting, I hadn't actually considered partial composition, since my language is very "at the metal" and partial composition isn't possible (or rather would have to have a macro and data-structure built around it). That's a good point for a "functional-style" language though.
Re:Why concatenative programming matters (2012) by adastra22 on Sunday July 17, 2022 @02:20PM (#32129489)
Partial compilation is pretty core to concatenative languages. I don’t know how you can call yours one without it.
Re:Why concatenative programming matters (2012) by vitiral on Sunday July 17, 2022 @05:16PM (#32131378)
You mean partial _application_. The author called FORTH a concatenative language, and FORTH definitely doesn't have partial application. In fact, FORTH doesn't even have a type system. Perhaps this is a miscategorization?

fngi does have a type system which modifies a stack. However, it will be lacking many high-level type system features, being similar (but smaller and lighter-weight) than golang. Not totally sure if it qualifies or not.

Re:Why concatenative programming matters (2012) by js8 on Sunday July 17, 2022 @09:00AM (#32126374)
But this is true for ML syntax too, isn't it? And ML-likes are widely considered to be readable.

There are also features of concatenative languages, like quoting in Joy or Factor, which can help alleviate the problem.

Re:Why concatenative programming matters (2012) by layer8 on Sunday July 17, 2022 @09:21AM (#32126531)
> But this is true for ML syntax too, isn't it?

In ML, function application is left-associative, and therefore `a b c` always means `(a b) c`. It can’t mean `a (b c)`, even if that would fit the types.

The expression structure is therefore independent from the types in ML. You can parse the expression tree without a symbol table. In concatenative languages, on the other hand, you need to look up the types, or if the language is dynamically typed, the expression structure only manifests at runtime.

Re:Why concatenative programming matters (2012) by js8 on Sunday July 17, 2022 @11:16AM (#32127528)
What I mean in ML syntax, every function is a curried function of only one argument. So you don't know how many arguments a function has without looking up its type. In concatenative languages, this is similar, every function (with exception of special forms) is a function from stack to stack, and to see how many arguments it has you also have to look at its type.

I think concatenative syntax is in fact more elegant than ML syntax, since it also "curries" return values, and is evaluated in the same order as written.

Re:Why concatenative programming matters (2012) by yakubin on Sunday July 17, 2022 @09:49AM (#32126727)
If b is declared to be infix by default, then it will be an application of function b to arguments a and c though. So you need to know the symbol table anyway it seems.
Re:Why concatenative programming matters (2012) by IHLayman on Sunday July 17, 2022 @09:33AM (#32126612)
Haskell coders have to do this often enough that there is a token that does this grouping… f $ g x = f (g x), so the expression `a b c` is still a source for ambiguity.
Re:Why concatenative programming matters (2012) by avgcorrection on Sunday July 17, 2022 @10:51AM (#32127296)
`$` is an operator. There is no ambiguity (you might not know the precedence table but that doesn’t make it ambigious).
Re:Why concatenative programming matters (2012) by pklausler on Sunday July 17, 2022 @12:27PM (#32128217)
`$` = id
Re:Why concatenative programming matters (2012) by zetalyrae on Sunday July 17, 2022 @09:09AM (#32126430)
I think currying by default is an antifeature and ML would be better off with f(x,y,z) call syntax. It interacts poorly with type inference: forget a parameter, or swap a parameter, and get an incomprehensible type error message about some function type that has little to do with the actual fault. This is prevented by parenthesizing expressions and using tuples as the sole argument.
Re:Why concatenative programming matters (2012) by yakubin on Sunday July 17, 2022 @09:16AM (#32126497)
> But this is true for ML syntax too, isn't it? And ML-likes are widely considered to be readable.

I don't see how that's true for ML. Could you give an example showing the ambiguity?

Edit: I think I can see how the presence of infix syntax may cause that. As in: when you see "a b c" it will read differently based on whether "b" is a function that's declared to be infix by default. Yeah, I think infix notation is a mistake. The more I think about syntax, the more appeal I see in S-expressions.

Re:Why concatenative programming matters (2012) by js8 on Sunday July 17, 2022 @11:20AM (#32127568)
In ML syntax, you don't know how many arguments a function will take, because of currying, and it's a (useful) feature, not a bug.

Similarly, in concatenative languages, there is ambiguity how many parameters will a function take/leave on the stack.

Re:Why concatenative programming matters (2012) by yakubin on Sunday July 17, 2022 @01:50PM (#32129175)
> In ML syntax, you don't know how many arguments a function will take, because of currying, and it's a (useful) feature, not a bug.

Currying doesn't change the picture here from the situation in, let's say, C. If it wasn't for "infixr", there would only be one way to parenthesise an expression. Just like in C, you could apply a function of N arguments to N+2 arguments, and the function call would get interpreted by the compiler as function application to N+2 arguments. It's just that there would be a type error, but gramatically, there is no ambiguity about which symbols are used as called functions, and which as arguments to the functions (and to which functions)... as long as you don't use "infixr". Same with a function of N+2 arguments getting N arguments. At some point you get a type error probably, because you're going to get a function, where you expected a non-function; but the type system doesn't influence the building of AST (in this place at least). Looking at the source you know how many arguments are getting applied to what, without even knowing the definitions of functions, as long as you don't play with infix functions.

To make an example, again ignoring infix stuff, given "a b c d e", I know to read the whole expression as application of function a to arguments b, c, d, e. To be even more literal, SML doesn't have multiple-argument functions, all functions take one argument, and I can write it as "((((a b) c) d) e)". Unlike in concatenative languages, the function application doesn't suddenly stop in the middle of this expression, just because a takes fewer arguments than it was given, e.g. this expression can never be interpreted as "((a b) c) (d e)", even if it made more sense from the point of view of types of a, b, c, d, e.

But when you do add infix syntax, then yeah, there is ambiguity, and now AST building is driven by the symbol table a bit.

Re:Why concatenative programming matters (2012) by js8 on Sunday July 17, 2022 @07:32PM (#32132630)
> To make an example, again ignoring infix stuff, given "a b c d e", I know to read the whole expression as application of function a to arguments b, c, d, e.

Yes, but what does it really tell you? It doesn't tell you that the final result is a primitive value, it might as well be a function again. (We need to be careful how we define an "return argument", whether it is only primitive types, or also function types, and based on the chosen definition, the ambiguity is present in both types of syntax, or in neither.)

Similarly, if I have a program "a b c d e" in concatenative language, it is a function from stack to stack, and we don't know, how many arguments it either took from or left at the stack when it's done, without looking at the types.

The point is, ambiguity regarding number of processed arguments is there in both cases. Except in concatenative languages, it also applies to the returned arguments (i.e. a "function" in a concatenative language can kinda take negative number of arguments).

I think what mainly confuses you is that you really don't need an AST with concatenative languages. And it cannot be created, it's not a tree, more like "abstract syntax sequence".

Re:Why concatenative programming matters (2012) by nmz on Sunday July 17, 2022 @11:00AM (#32127390)
Its seen as bad practice to use anything but one argument per word.

There's also quotations, retroforth[1] tends to use a prefix based system for indicating the type of the argument, c:put will print a character, s:put will print a string.

https://github.com/crcx/retroforth/blob/master/doc/NamingCon...

https://github.com/crcx/retroforth/blob/master/doc/QuickRef....

Re:Why concatenative programming matters (2012) by zozbot234 on Sunday July 17, 2022 @11:44AM (#32127798)
Yes, but this is exactly parent's point. Once you allow for the possibility of procedures taking multiple arguments, one has to deal with call trees which cannot be represented unambiguously and compositionally in linearized form. And if this is extended to multiple return values, the "tree" is replaced by a dataflow or control-flow graph (both are possible, and not directly compatible). The change from an ordinary concatenative language is quite substantial.
Re:Why concatenative programming matters (2012) by MaxBarraclough on Sunday July 17, 2022 @01:10PM (#32128733)
> Its seen as bad practice to use anything but one argument per word.

I've never heard this before. Many of the words in the ANS Forth standard [0] consume more than one element from the data stack, and it's hard to see how else they could be defined.

The '+' word consumes 2 elements, as we'd expect. So does '!'.

[0] http://lars.nocrew.org/forth2012/alpha.html

Re:Why concatenative programming matters (2012) by qgc on Monday July 18, 2022 @12:05AM (#32134529)
> Many of the words in the ANS Forth standard consume more than one element from the data stack

ANS generally frowned upon by Forthers, the idea of having a standard, cross-platform Forth is something of an insult the Forth philosophy. If you check out something like the Silicon Valley Forth Interest Group guys, they all make their own Forths, or use one specific to the platform they're using (such as Brad Nelson's Forth for the ESP32, if they're using that device.)

Additionally, I don't agree you should never use more than one, but the majority should only take one, the cases where you do take 2 or 3 should be either primitive words or fit the style and form of primitives that take in 2 or 3. (for instance we might see `cells!` take in 2 variables just like `!`, but it's styled like said primitive so it's behavior is clear and follows known patterns.)

It would be more helpful/accurate, to say that one should only have a maximum of 3 items on the stack at a time (which is what Chuck suggests) and so any time where a word takes in 3, it should be consuming the stack and replacing it with either zero or one items in return.

Re:Why concatenative programming matters (2012) by qgc on Monday July 18, 2022 @09:51PM (#32146580)
If you want to see Chuck talk about these kinds of things "1x Forth" [1] is a great talk from him on these kinds of things.

[1] https://youtu.be/pSnNy7IpVMg

Re:Why concatenative programming matters (2012) by MaxBarraclough on Tuesday July 19, 2022 @02:28PM (#32155938)
> ANS generally frowned upon by Forthers

That sounds like an overstatement. Opinions vary, yes, but many of the major Forths for desktop and server target ANS Forth: pForth, gforth, iForth, SwiftForth, and I think also VFX Forth.

Re:Why concatenative programming matters (2012) by qgc on Saturday July 23, 2022 @04:21AM (#32201428)
It's really not.

This isn't a difference of opinion that looks like "well some thing x is better at doing y than z" It's a fundamental disagreement on the purpose and practice of the language. For regular Forth users, the high and low level sit together with Forth, you don't experience a sense of any kind of "layers" of abstraction over the fundamentals of what the machine is and does. One can dispute whether Forth has this property, but it's really not an overstatement to say that regular Forth users feel this sense deeply and share this subjective experience.

The capabilities of processors change a ton over time, which is the most damning aspect of ANS Forth, it is simply outdated!

--

The "major forths" you speak of are exactly what is antithetical to the general forth philosophy of implementing only what is necessary above the lowest level. Even the term "major forth" really speaks to the bias that popular programming languages and their users have. And there's a really good reason for this, standard Forth words really just don't make any sense unless they give you access to the hardware in a way that cross platform software is largely antithetical to. Cross platform components in software fundamentally act as either a service to a platform layer or otherwise provide an abstracted interface to a varying implementations.

The whole point of a language like Forth is to turn the lights on, to play the audio and to drive the car. Chuck was wholly aware of higher level systems like Lisp when he created Forth and purposefully developed a tool that made it easy to start from first principles and develop a Forth system to suit your needs on a domain specific level. That is, the domain of the device and it's capabilities.

The closest thing to a de-facto standard is more like eForth.

> "eForth allows me to make a complete Forth system with about 30 very simple machine code routines. With so few words to code, I could do the coding by hand, that is, without the need to write an assembler first. After this simple model is running, it is desirable to code much of the rest in assembly." http://www.forth.org/eforth.html

Notably it is reasonable to have portable forths, in that some of the same patterns may emerge, (some primitives are obviously going to be the same, you'll always need dup), but they aren't cross platform in that the code simply doesn't translate. The words needed just aren't the same. You don't want to write reusable code, you should just rewrite it.

It's actually hard to overstate how disliked ANS Forth is, not only by it's creator but by the active community (there aren't many avid, regular users who aren't a part of some kind of interest group.)

I really do recommend 1x Forth which I posted in response to your first comment to get at the kind of simplicity that the Forth philosophy targets.

"The ANS Forth standard is at least one, maybe two orders of magnitude more complex than Mr. Moore's approach to Forth. He says that code should be so simple that most type of errors simply can't happen. In the late eighties and early nineties Chuck quit writing code in Forth and experimented with sourceless programming. His first versions of his VLSI CAD software, OKAD, were constructed without source using his tools in his OK operating system. Later he return to Forth programming and rewrote OKAD II under his new colorForth. [Moore, 2000] In the chat session Chuck was asked, 'How did you come to the conclusion that Forth was too complex, and that sourceless programming was your next move?'

His reply was, 'Maybe by reading the Forth Standard.'" http://www.ultratechnology.com/levels.htm

In Chuck's conception, it's the kind of simplicity that's necessary if you want to start putting computers in your eye. https://youtu.be/0PclgBd6_Zs

Re:Why concatenative programming matters (2012) by astrobe_ on Sunday July 17, 2022 @12:11PM (#32128052)
"One" would be troublesome... Two is actually common, three is in the red zone, four you refactor. Except in specific cases where 4+ arguments doesn't cause stack juggling.

Chuck Moore (inventor of Forth) said once that he don't use stack comments anymore. He claims that the arguments a definition takes should be obvious from the name or the definition (one-liners are common in Forth) or be documented somewhere else than in the code. Moore's claims are often bold and hardly believable. But when you put it to the test yourself, they sometimes do work for you too. This one does work for me.

The answer to the "cognitive overload" argument is kind of trivial: "if it hurts, don't do it". The long answer is that concatenative languages have their idiomatic ways of doing things like any other language. Don't be stubborn; practice and embrace it.