Forks is the answer to what is the main reason to use J, IMO. Unfortunately, the parsing and readability rules force you to understand and memorize hooks as well, and adding complexity without much benefit.
If you only use forks and never use hooks, then your code is much more readable because you don't have to count the number of verb phrases in a parenthesis to know how to parse it. More importantly, you don't need to remember/keep looking up how hooks are applied.
Forks have a pretty simple application rule that is consistent and natural in the monad and dyad case.
Applying a verb monadically even if it is parsed as dyad
will give the result of f y, even if it is called as x f y.
- 2 +@] 3
(f g) y --> y f (g y)
x (f g) y --> x f (g y)
Equivalent forks to hooks
(f g) y --> (] f g) y
x (f g) y --> x ([ f g@]) y
It turns out that a single conversion is possible for both the monad and dyad cases, but its rare that a hook (or fork for that matter) has an obvious sense for both its monad and dyad case, and so I prefer to tailor syntax to its intended use. The common conversion of a monadic and dyadic hook is the dyadic one:
(f g) --> ([ f g@])
This works for the monad case as well because [ y --> y.
An arguably cleaner version is:
(f g) --> (f [: g ]) --> ([ f [: g ])
Even if you like to continue to use hooks, these conversion concepts can help you build longer trains where the hooks are not limited to the leftmost position. Understanding the conversion techniques can also let you modify the mechanics of forks. For instance:
(f x) h (x g y) --> x (f@[ h g) y ---> (g h~ [: f [)
The main reason I hate hooks is that if I haven't looked up the definition in a while, though hooks have been defined with the arguably most useful semantics, an alternately natural verb execution/definition for x (f g ) y is f x g y . When the f is something like #, its easy to mistake it as being count instead of select. The msitake is only avoided if you spot that it is a hook and not a fork, and have the hook parsing definition memorized.
f x g y --> x ([: f g) y
y f x g y --> (] f g)
x f x g y --> ([ f g)
Thinking with forks only I believe lets me focus better on a problem without considering how to try to force the solution into a hook. It lowers my fear of dyads, because each side of a fork has easy flexibility to to use any part of the fork's dyadic arguments in any order.
A selection example
selection in J is the most common use of a hook
(#~ 3&>) i.6 will select those items smaller than 3 from y
3(>#]) i.6 does the same. Is shorter. Does not include the ~ adverb which does very different things in different contexts, and provides a natural place to edit if you want items smaller than 4 or other selection parameters.
(]#~3>]) also does the same without using up a dyadic argument that could be of use to the rest of the train. The leftmost ] is optional and creates an equivalent hook( if monadic call). The advantage of this form is avoiding a conjunction (&) that might be confusing to reader. More importantly, it has the same result regardless of being called dyadically.
If the language designers were to remove hooks from the language, the most natural re-definitions of the verb (f g) would be
([: f g). --> f x g y (or f g y if monadic)
(] f g) ---> y f x g y (or y f g y if monadic)
I doubt its worth a language change, but to me, its worth never using hooks.
If hooks are not used much, dyadic u&v is likely used even less: It is (v x) u (vy). fork version to replace &:
(f&g) --->(g f~ [: g [) for dyad. ([: f g) for monad
The (N V) = N&V proposal
It has been proposed as a way to remove syntax errors to parse (noun verb) as noun & verb
A nice feature of the proposal is that it would make code more readable and require fewer parentases when you normally avoid the use hooks:
(2 + * ]) 3 = 15
5 (2 + [ * ]) 3 = 17
With this proposal, it would be tempting to also make the train (f g N h) equal to the fork (f g N&h)
to be consistent though, (u v) should also be equal to u&v. In the monad case, it is ([: u v). In the dyad case, it is much weirder (v@:[ u v). I would prefer that dyad (u v) also be equal to ([: u v) ie. u x v y