What you don't get in FORTH is arity information from the syntax, because everything is simply treated as a function from stack to stack. Unfortunately it's not feasible to keep the cleanliness of the FORTH syntax while providing that info (one would need a 2D view for that, where multiple arguments to the same function can be seen in parallel) so a parens-based syntax is arguably the next best choice.
The other thing you can't do (straightforwardly at least) is push a word onto a stack or apply it against some kind of compound structure.
That's what I mean by Forth words not really being functions, they're subroutines for sure, and you can do some surprisingly powerful metaprogramming given that what you have is a glorified linked list, something a little more powerful than GOSUB, and two stacks, but functions?
When you compare it with a visually similar concatenative language like Factor or Joy, which inarguably have functions, Forth words look pretty different.
a b c d e f g h i j
and (a (b c d) (e f) (g h (i j)))
are about as meaningless as each other to me with just letters, in Forth you build a sort of minimal vocabulary for the problem at hand and so you have a better idea of what words are doing. Proper documentation (or at least very good naming) is needed in lisp and forth IMHO.Of course you actually have to know what the words mean. But it's harder in Forth than in Lisp or conventional C-like languages because Forth takes away all of the visual cues that help you figure out what is going on.
That's true, and it is arguable that Lisp could be improved by making macro invocations have a distinguished syntax.
But in practice it's usually pretty clear what is a macro and what is a function because of naming conventions, and because macros commonly admit syntax that would be illegal in a function call e.g. (let ((x 1)) ...) But however bad this problem is in Lisp, it's much worse in Forth.
> Forth doesn't really have functions
That is debatable. If I write 1 2 + 3 * then I think of the + and the * as function invocations that take their arguments off the stack and leave their results on the stack. It's true that this is not exactly the same as a function call in Lisp, but it's similar. In any case, if I write:
a b c d e f g h i j
there is absolutely no way to tell what that is going to do without knowing what every letter means. By way of contrast, if I write:
(a (b c d) (e f) (g h (i j)))
then it's a pretty good bet that a is a function that takes three arguments, b and g are functions that takes two arguments, e and i are functions that take one argument, and c d f h and j are variables.