4 ms·
How does one deal with complex if-else statements in a concatenative language?
by simplify 11y ago
How does one deal with complex if-else statements in a concatenative language?
- PeCaN 11y agoYou usually just avoid them. Some (e.g. Factor) do offer pattern matching/multimethods which are generally much cleaner.
- simplify 11y agoI try to avoid them too, but sometimes business requirements are just too particular to do so.
- astrobe_ 11y agoIt sure can be a problem when you have to deal with "business requirements" that you can't change, because in Forth you look for simplifications at every level. Dealing with that may require to thing out of the box a bit, and forget about what is "good" and "bad" in other languages. Maybe you can use table-driven programming? Maybe you can do some dirty tricks with the return stack to get the control-flow you want? Maybe you really have to put that data in a variable? There's no single answer, and it can get ugly at times. Not, BTW, ugly like in the article. This: > : apply2 rot dup rot dip rot apply swap ; ... is an abomination. "dip" is questionable already. "rot", thought part of the standard, is best avoided. Because one should only deal with the top two elements of the stack. And of course "apply2" itself is suspicious. Why the author showed this, even though he seems knowledgeable enough to know that he shouldn't? Because he picked a simplified Forth that did not let him use the return stack. The author sure talks a lot about the "Forth philosophy" but apparently missed one or two things about it. If one wants to talk about philosophy, one should at least include in the bibliography what his inventor says about Forth (http://www.ultratechnology.com/forth0.htm http://www.ultratechnology.com/forth0.htm - a lot of stuff there, the main piece being 1xForth). I believe it's Moore that said something like "The important thing is not what you add, but what you remove". The return stack is a core component of Forth; picking a "simplified" Forth that won't let you use it ... well that language shouldn't be called Forth to begin with. That's a bad choice. There are also other questionable choices like using [ ] for "quotations" when they are used for something totally different in standard Forth. Introducing "quotations" is also questionable in itself. It only looks easily implemented because Ruby provides all the memory management; but in reality implementing an interpreter on top of an interpreter is not viable (that makes like at least a x1000 slowdown relative to C); Forth interpreters are typically written in low level compiled languages (typically C), which don't provide garbage collection. And in the end, those "quotations" only look cute. What really helps is currying. Forth is ugly, stupid and mean. It's up to you to find clever ways to write nice-looking programs.
- rkallos 11y agoFactor has the words if, when, unless, cond and case. That should usually be enough for anyone's needs. That said, I suspect that nesting conditional statements 3 or even 2 levels deep would become difficult to read. As a result, it'd probably be best to factor out various tests into their own words to keep things readable.
- aidenn0 11y agoA brief introduction to conditionals in forth is here: http://www.forth.com/starting-forth/sf4/sf4.html http://www.forth.com/starting-forth/sf4/sf4.html
- munificent 11y agoForth has "compile time words". These are sort of like macros in that they are evaluated/expanded at parse/compile time. From what I understand[1], they are generally replaced with special branch instructions that move the instruction pointer. So this line of Forth: 3 4 < IF ." OK" ELSE ." WAT" THEN Means: Evaluate "3 < 4". If it's true, print "OK", otherwise print "WAT". The "IF" "ELSE" and "THEN" words are compile time words. When that line of code is entered, before it's run, they get expanded to something like: 3 4 < ?BRANCH 5 LIT "OK" OUTPUT BRANCH 3 LIT "WAT" OUTPUT Here, "?BRANCH" is a conditional jump followed by an offset and "BRANCH" is an unconditional jump. Those are built-in and will move the instruction pointer directly, so you get the conditional evaluation you need. Other stack based languages do this differently. In Factor, it would be: 3 4 < [ "OK" print ] [ "WAT" print ] if Here, "[" and "]" are "parse words" that at parse time wrap of their contained words and create a quotation out of them. Basically like a closure or thunk—a chunk of code to be executed later if desired. So this code evaluates the condition and puts it on the stack. Then it pushes two quotations onto the stack. Then the "if"[2] word executes. It's just a regular word now. It looks at the condition and, if true, executes the first quotation. Otherwise it runs the second. In this sense, Factor works like Smalltalk where control flow doesn't need to be special because the language has very terse syntax for explicitly creating a deferred chunk of code[3]. [1]: https://en.wikipedia.org/wiki/Forth_(programming_language) https://en.wikipedia.org/wiki/Forth_(programming_language) [2]: http://docs.factorcode.org/content/word-if%2Ckernel.html http://docs.factorcode.org/content/word-if%2Ckernel.html [3]: https://www.gnu.org/software/smalltalk/manual/html_node/Conditions.html https://www.gnu.org/software/smalltalk/manual/html_node/Cond...