First, there were obligatory stack effect declarations on each word. Want to refactor your program? Sure, rewrite your words, together with stack effect declarations. That part was extremely annoying when it came to exploratory programming. For the uninitiated: stack effect declarations are akin to function prototypes in C, only lacking type information. IMO, once you have them, and they're obligatory, you get all the drawbacks of postfix languages, and no benefits.
Second, the language and standard library rely heavily on stack combinators. Reading the standard library code requires intimate familiarity of what words like bi, bi@ and bi* do (among a whole lot of others).
Third, I find Factor's documentation extremely confusing. For example, there are "Vocabulary index" and "Libraries" sections, with little or no overlap between them. But vocabulary is a library (or package, whatever, same thing), so WTF?!
Then there are important and powerful features like generic words, but if you click on the "Language reference" link on the docs homepage, you get a menu with no mention of generics, and you have little clue in which section to look for them. (It's under objects.) Then you eventually find out (sorry, I was unable to dig up reference to the docs) that generics support only single dispatch, and only "math" generics (plus, minus, etc.) support double dispatch.
In short, the manual is a maze of cross-references with no head or tail.
Fourth, I dislike writing control structures in postfix. This is the part that, IMO, RPL on HP's calculators got right. Instead of forcing you to write something like
10
"Less than 10"
"Greater than 10"
[ < ] 2dip if ! 2dip is a stack combinator, guess what it does!
you could write IF 10 < THEN "Less than 10" ELSE "Greater than 10" END
(Postfix conditionals were available for the rare cases where they were the most convenient form.)Last but not least, it supports only cooperative threads.
The postfix notation even for conditionals is really not an issue when you dive into the language. However, with most systems you should be able to translate IF into 'nop', THEN into IF, and END into THEN and it should do the trick. But then there are DO WHILE loops. etc. But everyone prefers to preserve the postfix logic at the end of the day. It's like infix notation systems in Lisp and Scheme that never caught up (and, I believe, never will).
About your second point: most of the library code, in any language, looks like gibberish and like a mess of arbitrary idioms. One simply cannot hope to inspect and understand non trivial library code just just by casually reading it. With Forth the situation is indeed even worse, because implementers generally cannot resort to spaghetti code too much, whose continuous nature, at least, helps the casual reader.
On the topic of cooperative multithreading, I think that the trend in recent programming languages in general is that, nobody wants to deal with pre-emptive threads anymore. It hurts kitties, it is dirty. It is like manual dynamic memory management. Cooperative threads (if I'm not mistaken, they're also sometimes called "lightweight threads", "green threads" or "fibers"), avoid most of the problems of preemptive threads (because the programmer controls when execution can switch to another thread and when it should not very easily), while retaining some benefits (background execution of low priority tasks). For a "modern" Forth system, I think it could be interesting to keep cooperative threads at user level (really hardcore implementers may just remove them completely, because it is expected that the programmer may prefer to implement some sort of cooperative tasking fitted to his needs), and use preemptive/OS threads in library code (typically implemented in C).
Now that's the point of view of a 'hardcore' kind of hobbyist Forth user (and implementor). There is another category that finds brutal simplicity simply not practical and I can understand that. But really for educational/curiosity purposes I would recommend the hardcore way. Like, abandon all the bloat you're addicted to, shut the up, try hard and sincerely. I tried Forth this way because I was very sceptical when reading Moore (inventor of Forth) and Fox (one of his collaborator and friend; Moore isn't very good at communication, so Fox [RIP] was sort of his Plato) that their approach could work. These experiments in extreme simplification brought me a lot when I was a young programmer.
10 < [ "less than 10" ] [ "greater than 10" ] if
This is pretty easy to read. If the boolean condition is true, run the first quotation, if it is false, run the second.The unusual names lke 'dip', 'bi', etc are just common names that once learned make code easier to read. 'dip' is from Joy IIRC.
I do agree that combinator heaviness can make things difficult for a beginner. Especially if code uses a combinator that isn't common it requires time to look up and see what it does. You really need to immerse in Factor development for a while to get familiar with them.
Stack effect declarations exist to make refactoring safer. Prior to having them enforced by the compiler it was common to change a words stack effect only to find it broke random code using it elsewhere. Making the check enforced provided a safety check that you hadn't forgotten to change word usage elsewhere.
The refactoring problem could have been solved in a more practical way (though more difficult implementation-wise) by implicit versioning of symbol definitions. Plus, I find that stack effect declarations read like line noise (see for example the declaration for if; IIRC from browsing the docs it's one of the moderately bad ones.)
That would be because Factor is OO.
: names ( -- seq )
"names.txt" ascii file-contents [ quotable? ] filter "," split ;
: score ( str -- n ) [ 64 - ] map sum ;
: solution-22 ( -- n )
names natural-sort [ 1 + swap score * ] map-index sum ; "hello world" print
This is also why ternary expressions in C like languages is hard to read.I see no semantical difference between
if(expr1) expr2 else expr3;
and expr1 ? expr2 : expr3;
Okay you need to learn the syntax, but it should be second nature after a few times. I also think the question mark is very intuitive. "hello world" uppercase print
While in languages where function application is prefix, you have to go inside to outside, right to left: print(uppercase("hello world"))
Additionally, if there are infix operators, the direction is switched mid statement: print(uppercase("hello" + "world"))
hello -> world <- uppercase <- print object.method()
"hello world".print()
If you're used to speaking a language like English where the word order is subject-verb-object, you'd probably consider a subject-object-verb language to be "backwards" too. But native speakers of that language certainly wouldn't have a problem.I'm sure it's just a matter of what you're used to.
Next, there's the idea of the stack. Every word is executed, one after the other, and the words operate on the stack. So the four-word sequence 3 4 + . puts a 3 on the stack, then puts a 4 on the stack, then pops the top two numbers from the stack and puts their sum on the stack, then prints the top number on the stack. So the result, naturally, is 11. (Well, it is if you're operating in base 6 at the time. Didn't want to make it TOO easy for you!)
Oh, and there's also the concept of interpreting vs compiling. The colon word, which is spelled :, reads the very next word from the input and begins the definition of that word. Every word it encounters up to the next semi-colon is compiled, not executed. So if I did this
3 4 + : FOO SWAP DROP ; .
... it would still print out the sum of 3 and 4, but in between it would also define a new word called FOO. Which would be a silly way to program, but it demonstrates the point.So the short form is: begin reading at the top left, continue rightward and downward until you reach the bottom. There's no syntax as such, like Perl's die "Can't open file" unless $fileopen; because Forth-like languages don't read ahead to the end of the line.
[Edited: HN doesn't do Markdown. WTF? Also, I don't know my left and right.]
And it's a very coherent idea. It's meant to respect processor architecture while maintaining a good level of comprehensibility to the human. And it keeps a MUCH closer correspondence between "word" (essentially a routine) and actual instruction. That got WAY lost with C, enter the beast-compiler.
I think of it as a concept layer above assembly, that has features far beyond just mnemonic value. It's like Lisp to me- a language whose "syntax" is dictated by the functional necessity of a computing methodology, not the decisions of language designers. For me at least, the approach leads to much higher productivity for a variety of reasons.
There's also a very high probability that Forth is the first thing to be run when you turn on the computer or cellphone you're reading this from.
Now as to Factor... see my post above and help me out!
Thinking about Python, looks wise it is kind of the opposite of any of the stack languages. I get the feeling that you can like one or the other.
1) various 8-bit BASICs, 6502 ASM, Modula-2, 370 ASM, C, Ada, foxbase, SQL, Postscript, Forth, Objective-C (weird listing it out)
Firing up the Factor GUI environment you can access help via F1 where two options are the cookbook and the "Your First Program" tutorial.
This same documentation available in the environment is also available online[1].
[1] http://docs.factorcode.org/content/article-first-program.htm...
I could never get over the syntax though - even though the #factor folks say its a matter of getting used to and theoretically not "harder" than Lisp, Lisp feels very natural to me, but Factor always required a big overhead.
[1] http://www.latrobe.edu.au/humanities/research/research-proje...
I have always admired the engineering work on Factor, but had thought the project would die when Slava Pestov took his job at Google. Great to see it continue to improve.
On ARM it would be interesting- or if it had a highly modular structure that would allow it to be "easily" ported to other microprocessors.
But on x86? What niche is it trying to serve? Is it a language-lover's-language? I guess what I'm asking is, why would I use a "high-level" Forth on x86? There has to be a reason, I'm just missing it.
I used to run a video sharing website written in Factor [2], sadly the server crashed a few weeks ago and I haven't recovered the data from the disk yet.
Factor feels more like a Scheme than a Forth to me when programming.
[1] http://web.archive.org/web/20111117015025/http://www.bluishc... [2] http://web.archive.org/web/20130118204336/http://tinyvid.tv/
Currently working on an ARM forth-idea with some scheme-ideas. There's this nagging intuition I have that there's a symbiosis between Lisp atom/Forth word and S-expression/Forth dictionary.
I have to get something going pretty quickly so I can't get too pure about the idea, as I don't want to get bogged down in the necessary GC yet.
Thanks for your reply, it already made me more interested in Factor from an implementation standpoint, even if I wouldn't use it on x86.
This was very useful in the early days of Factor when language and base library changes caused bitrot in contributions. If the contributed library wasn't updated it was moved to an 'unmaintained' directory for later correction.
"hello world" print
...although, more realistically, you're probably interested in something like the gettings started section of the docs [1]. There are also some great short example posts on the planet feed [2] from various blogs.EDIT: I'll plug my own blog for this too, as I started writing a Beginning Factor series of posts a long time ago [3], but never got beyond a couple entries.
[1] http://docs.factorcode.org/content/article-handbook.html
[2] http://planet.factorcode.org/
[3] http://elasticdog.com/2008/11/beginning-factor-introduction/
From your second article[1] introducing Factor
"Next time we’ll stick with a theme of “flow” and discuss control flow for your words and after that, the typical work flow when developing code with Factor. Hope you enjoyed this installment!"
I am still waiting for that article. Your introductions to Factor are awesome and more would be delightful.
[1] http://elasticdog.com/2008/12/beginning-factor-shufflers-and...
https://github.com/raganwald/homoiconic/blob/master/2008-11-...
Personally, I've fallen in love with it a few years ago. Even though I don't use Factor for anything practical, the language itself (as well as Forth) has taught me a completely new way of thinking about problem solving. Something that I benefit from in other languages I use.
So if there is one reason you should at least check it out, it is that it will make you a better programmer. And I think anyone who's been writing code long enough, will know that there is always room for self-improvement.
"10:10:24 <RodgerTheGreat> another HN gem: "I must say, that the syntax and semantics are not intuitive at all. I've spent half an hour trying to learn how does this thing work, and have not been able to.""
It's horrible to watch when a language community has this sort of a elitistic attitude, especially when it's doubtful that the language has any future.