>>  <<  Usr  Pri  JfC  LJ  Phr  Dic  Rel  Voc  !:  Help  J for C Programmers

                                                                                               37. Introduction to Forks

Before we learn the rules for making tacit forms, you should understand why you are going to the trouble of learning how to write programs that hide their operands.  First, they are extraordinarily compact.  The explicit definitions we have written so far are laconically terse by the standards of most computer languages, but they will seem positively windy compared to the tacit forms.  Second, the shorter definitions are easier to combine with other verbs and with the modifiers that add so much power to J expressions.  Third, the tacit definitions are parsed when they are defined, in contrast to explicit definitions, in which each line is parsed as it is executed; we reduce interpretive overhead by using tacit forms.  Fourth, in learning to write tacit verbs you are also learning to write tacit adverbs and conjunctions, with which you will be able to craft your own private toolkit of modifiers that you can use to combine verbs in ways that are useful to your application.

In what follows, Nx will represent a noun, Vx a verb, Cx a conjunction, and Ax an adverb, where x is any suffix.

We begin by observing that the rules we have learned so far give no meaning to some combinations of words.  Consider three verbs in a row, with no noun to operate on, as in the sequence

   (V0 V1 V2)

where each Vn represents a verb--an example would be ((+/) % #) .  Without some special rules, we have no way to interpret this sequence.  Such sequences of words that cannot immediately be executed to produce a result are called trains.  Examples are C0 C1 A2, V0 V1, and the V0 V1 V2 we are considering now.

Understanding tacit programming will largely be a matter of understanding how trains are parsed and executed.  You will learn that (V0 V1 V2) is a new verb that can be applied to noun operands, and you will learn how it applies to nouns.  To begin with, observe that there is no reason that (V0 V1 V2) N should be the same as V0 V1 V2 N which as we know is (V0 (V1 (V2 N))) .

The meaning J assigns to (V0 V1 V2) Ny is:

   (V0 V1 V2) Ny  is  (V0 Ny) V1 (V2 Ny)

This substitution goes by the name monadic fork.  I think finding this definition was a stroke of brilliance in the design of J.  An example of the use of the fork is:

   (+/ % #) 4 6 8

6

which calculates the mean of the operand.  It is processed using the substitution rule above as

   (+/ 4 6 8) % (# 4 6 8)

6

which divides the sum of the items in the list by the number of items in the list.  You can use fndisplay to help yourself see how the substitutions are made:

   defverbs 'plus"0 div"0 tally'

   (plus/ div tally) 4 6 8

+---------------------------------+

|(4 plus 6 plus 8) div tally 4 6 8|

+---------------------------------+

The sequence (+/ % #) is a verb.  It can therefore be assigned to a name:

   mean =: (+/ % #)

or

   mean =: +/ % #

and then used by that name:

   mean 12 18 24

18

Impressive, isn't it?  With just 4 symbols we described a program to take the mean of a list of numbers (or a list of lists...).  The beauty and the power are in the way the operands and verbs are connected; that's what we'll be learning in the next few chapters.

At this point you may be impressed with the economy of the monadic fork but a bit confused about the details.  For example, we said that (V0 V1 V2) Ny is not the same as V0 V1 V2 Ny and yet we said that mean =: (+/ % #) is the same as mean =: +/ % # .  How can that be?  If we use the version without parentheses, why doesn't mean 12 18 24 get evaluated like

   +/ % # 12 18 24

0.333333

?

I could give you a simple rule of thumb, namely that you can always imagine an extra set of parentheses around any value assigned to a name.  That would be true but misleading, because it would encourage you to think that the values of defined names are substituted into a sentence before the sentence is executed.  That gets it backwards: in reality the operands are supplied to the stored definitions, which include compounds defined in the sentence.  The execution of a J sentence is a subtle alternation between creating definitions and executing them.  We will take the next couple of chapters to give you a thorough understanding of execution, after which we will return to see what magic we can work with forks and their brethren.


>>  <<  Usr  Pri  JfC  LJ  Phr  Dic  Rel  Voc  !:  Help  J for C Programmers