Help / JforC / Verb-Definition Revisited
>> << Pri JfC LJ Phr Dic Voc !: Rel NuVoc wd Help J for C Programmers
18. Verb-Definition Revisited
Before we discuss verbs in more detail, let me point out that the colon character (:), which you have seen used mostly as a suffix to create new primitives, has its own meaning when it is used alone or as the first character of a primitive: : is a conjunction, and so are :. and :: . The dot (.) also has a meaning but we won't be discussing it for a while. If you want . or : not to be treated as a suffix, make sure you precede it by a space: 3 : 'y' defines a verb, while 3: 'y' applies the verb 3: to the noun 'y' producing the result 3 .
What really happens during m :n and verb define
Now that you're getting older it's time we had a little talk. About where verbs come from. Up till now the verb-definition sequence has been exotic and peculiar, with a unique form; a possessor of great power but shrouded in mystery. We know only that it somehow breathes life into lines of text:
name =: verb define verb definition )
or
name =: verb : 'verb definition'
leaving name a verb.
With a layer of cosmetics scrubbed off it is more approachable. In both cases the conjunction m :n is at work. m must be a number, and n may be either a character string or the special number 0 (a list of boxed strings is also possible but we ignore that, considering it a curiosity). m :n executes the conjunction : with the arguments m and n; as with any conjunction, the result of this execution is an entity which can be any of the primary parts of speech (noun, adverb, conjunction, verb). m indicates the part of speech of the resulting entity: m=0 means noun, 1 means adverb, 2 means conjunction, 3 means verb, 4 means dyadic verb; 13 is also possible and you will learn about it when you study Tacit Programming. You can remember the numbers 0-2 because nouns take no operands, adverbs 1, and conjunctions 2; verbs are last in the precedence order so they get number 3, and dyadic verbs with their extra operand get number 4. If n is a character string, it supplies the text of the entity (which, again, is given the part of speech indicated by m); if n is 0, the interpreter interrupts the execution of the : conjunction and reads lines from its input source until it finds one that contains only the word ')'; execution of the interrupted sentence then continues, with the text of those lines becoming the right argument to : and thence the text of the defined noun/adverb/conjunction/verb. If the verb is defined in a script file, the input source (for lines to satisfy m :0) is the script file; otherwise the input source is the keyboard.
So what's with this 'verb define' lingo? Simple: when you start J, you get a few names defined automatically, and verb and define are two of them--as you can see by asking the interpreter what their values are:
verb 3 define :0
--so when you use verb define you are really executing 3 :0 to produce a verb; similarly dyad : 'one-line definition' is the same as 4 : 'one-line definition' which executes the : conjunction to produce a dyadic-verb result.
The point to note is that a verb-definition sequence is just an instance of a compound verb produced by a conjunction, and the resulting verb can appear anywhere a verb is allowed; when the resulting verb is executed, it operates by interpreting the text of the definition one line at a time. You may assign the verb to a name but that's not required. Here are some examples showing how a definition is just an ordinary part of a sentence:
addrow =: monad : '+/y' "1
We define the verb, using the : conjunction, and then we use the " conjunction to create a verb with rank 1. This is a principle to live by: Always make sure any verb you define has the proper rank. Following this rule will save you untold trouble by guaranteeing that the verb is applied to the rank of cell that you intended. The verb produced by the : conjunction has infinite rank; here, we expect to apply addrow to lists, so we create a rank-1 verb that we then assign to the name addrow .
(3 : '+/y') i. 6 15
See? we define a verb, then we execute it. It doesn't have to be assigned to a name. The distinction between code and data is not sharp as it is in C.
a =. '+/';'*/' b =. (0{::a),'y' (3 : b) 1 2 3 4 5 6 21
b is '+/y' so we can use it as the text of a verb which we then execute.
b =. (1{::a),'y' (3 : b) 1 2 3 4 5 6 720
Here b is '*/y' . In a later chapter we will explore the delights that result from being able to change the text of a program while you are executing it.
Remember: make sure the verbs you define have the proper rank.
Compound Verbs Can Be Assigned
Since we now understand that verb define is just a conjunction producing a verb result, and we know that we can assign its result to a name, we wonder whether we are allowed to assign any compound verb to a name. Yes indeed, we can, as we saw in the assignment to addrow in the previous section. Any verb can be assigned to a name:
dotprod =: +/@:*"1
Dot-product of two lists is the sum of the products of respective pairs of elements in the lists.
1 2 3 dotprod 1 2 3 14
dotprod takes the dot-product.
veclength =: %:@:(dotprod~)"1
The length of a vector is the square root of its dot-product with itself.
veclength 1 2 3 3.74166
Dual-Valence verbs: u :v
u :v also defines a verb of infinite rank but it is completely different from m :n . The defined verb is u if it is used monadically, but v if used dyadically:
bv =. (monad : '+/y') : (dyad : 'y - x') bv i. 7 21 2 bv i. 7 _2 _1 0 1 2 3 4
You can see that the appropriate valence was chosen based on whether the verb was executed as a dyad or as a monad.
u :v is often used to assign a default left operand to a dyadic verb:
pwr =: 2&pwr : (dyad : 'x^y')
If you execute pwr as a dyad you get x^y . If you execute it as a monad you get 2 pwr y which is then executed (using the dyadic valence of pwr) to become 2 ^ y :
3 pwr 4 81 pwr 4 16
The Suicide Verb [:
The verb [: fails if it is executed. Use it as one side of u :v to produce a verb that fails if it is used with the wrong valence. Example:
i. 100 110 120 130 |out of memory | i.100 110 120 130
Oops, we thought we were looking those values up in a list. We're lucky that we just ran out of memory--sometimes using the wrong valence of a verb can have catastrophic consequences. To prevent it, use
dyadi =: [: : i. dyadi 100 110 120 130 |domain error: dyadi | dyadi 100 110 120 130 100 130 150 dyadi 100 110 120 130 0 3 3 1
[: also has a special meaning when it appears in a fork, which we will encounter later.
Multi-Line Comments Using 0 :0
We know that all comments in J start with NB. and go only to the end of the line. There is no way to extend a comment to more than one line, but there is a way to put a series of non-executing lines into a script without having to have NB. in each of them. You simply define a noun using 0 :0 and put your comment inside the noun:
0 :0 This is the first line of a comment. The lines of the comment will become a noun; but, since the noun is not assigned to anything, it simply disappears. )
Final Reminder
Remember: make sure the verbs you define have the proper rank.
>> << Pri JfC LJ Phr Dic Voc !: Rel NuVoc wd Help J for C Programmers