Help / JforC / When Programs Are Data

From J Wiki
Jump to navigation Jump to search


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


                                                                                 24. When Programs Are Data

One characteristic of maturity in programming is readiness to pass a program as an argument.  A well-designed program does not exhibit a megalomanic urge to do everything the user may desire; it is content to perform a limited function well, and to leave other functions to other programs.  A suite of such programs can be variously connected to perform a great variety of functions, with each program doing its bit and passing control to the next one.

In C a program is passed to another program by pointer reference, and invoked by (*pfi)(arguments).  J has no pointers, but it has a great many ways to pass executable nuggets around the system.  We will learn them now.

Calling a Published Name

The simplest way to get a verb f to pass control to another program g is to define f to call a verb with a public name, say f_subfn, and then to define f_subfn to be .  In C, this would be ridiculous, because it would imply that f_subfn is permanently bound to a single value of g, with the result that all calls to f from anywhere in your program would be stuck with the same value of .

J overcomes this objection by allowing redefinition of f_subfn .  Each invocation of f looks like

   f_subfn =: g
   f arguments

When f is invoked elsewhere, the correct value of f_subfn will be assigned similarly, so each invocation of f can pass control properly.

I find this technique hideous, but I have to admit it is effective.  J uses it to get callbacks from DLLs.  It must not be used if f_subfn is going to be called in response to an event, since its value may have been redefined by the time the event occurs.

Using the Argument To a Modifier

If the verb isn't going to call a name of its own choosing, you have to tell it what to call.  The simplest way to do this is to change the verb into a modifier; then when it executes it has access to its operands, which can be verbs.  So, instead of

f =: verb : 'definition'

you write

f =: adverb define
monadic definition
:
dyadic definition
)

and within the definition of f you can refer to the left operand of the adverb, which goes by the name .  If you decide to write a conjunction instead, its right operand is .  The noun operands of the derived verb (u f or u f v) are x and y, as usual.  We will discuss user-defined modifiers in a later chapter.

In Chapter 3 this technique was used to calculate the Chebyshev coefficients of a function.  The function to be approximated is one of the inputs to this calculation, so we write an adverb and let its left operand be the function.  With chebft defined as in the example, an example of its use is:

   10 (2&o.) chebft 0 1
1.64717 _0.232299 _0.0537151 0.00245824 0.000282119...

Here the function to be approximated is the cosine function 2&o., evaluated for 10 Chebyshev coefficients over the interval 0 1 .

When you define a modifier, you have no way to specify that you are defining only the dyadic case as you can for a verb with dyad define .  Instead, you use the form given above, in which the monadic definition (if any) is separated from the dyadic by a line containing a single ':' character.  chebft did this, since its derived verb is always dyadic.

Invoking a Gerund: m`:6

Sometimes you want to use a noun rather than a verb to designate a verb to be called.  An asynchronous socket handler is an example: the socket handler will have many transfers going on at once, each one with a callback to be executed when the transfer is complete.  The callbacks must be put into a table along with other information about the transfer; in other words, the callbacks must be nouns.

We have already met nouns that carried verbs; we called them gerunds.  We found that gerunds were created by the conjunction ` and executed by m@.v .  While these tools are adequate to allow verbs to be passed as arguments, some simplifications are available that we will discuss now.

A gerund created by u`v is always a list (each element of which, as we learned, is an atomic representation of a verb, which we will treat as untouchable).  Even if there is only one verb, the result of ` is a list:

   +`''
+-+
|+|
+-+
   $ +`''
1

It makes sense for the gerund created by ` to be a list, since it contains a list of verb-representations one of which is selected for execution by m@.v .  But when we are passing a single verb-as-noun as an argument, it is OK for it to be a scalar box.  And, it can be invoked using the `: conjunction: m`:6 converts the gerund m into a verb.  So, in our example, we pass the callback as one box in a parameter list, and then we select it, turn it into a verb with m`:6, and execute it, as we can see in a stripped-down example.  The gerund operations are not difficult so I am going to keep your interest with a couple of new tricks:

   callback =: dyad : '(x) =: y'

Howzat?  (x) =:?  Yeah, this means that the value of x tells what variable will be assigned.  x can be any valid assignment target: a name, a multiple assignment, or other exotic forms given in the Dictionary.

   calledfn =: monad : '(0 { y)`:6 (1 {:: y)'

So calledfn is expecting an argument of (at least) 2 boxes.  The first one will be the gerund to execute, and the second one will be the argument to pass to that verb.  We open the second box (with {::) but we leave the first as a box so that `:6 can turn it into a verb.

   calledfn 'vbl'&callback ` (<25)

The first challenge is figuring out what the argument to calledfn is.  ` sees a verb on its left; it converts that to a gerund.  It sees a noun to its right, so it appends it unchanged to the gerund from the left.  This produces

   'vbl'&callback ` (<25)
+----------------------+--+
|+-+------------------+|25|
||&|+-------+--------+||  |
|| ||+-+---+|callback|||  |
|| |||0|vbl||        |||  |
|| ||+-+---+|        |||  |
|| |+-------+--------+||  |
|+-+------------------+|  |
+----------------------+--+

The first box is the atomic representation of the verb, just as mysterious as it was billed to be, and the second box has the 25.  Now what do we get when we pass that in as the argument to calledfn (try to work it out before you peek at the answer)?

   calledfn 'vbl'&callback ` (<25)
25

Did you get it?  We executed 'vbl'&callback 25 which then executed vbl =: 25 which has the result 25, which comes back as the result of calledfn .  The assignment was public:

   vbl
25

Passing the Definition Of a Verb: 128!:2 (Apply)

As an alternative to passing a gerund and invoking it with m`:6, you could pass the string representation of a verb and make a verb out of it with 3 :n or 4 :n .  Better yet, if you can make do with a monadic verb, you can use the foreign dyad 128!:2 which has rank 1 _ and goes by the name Apply.  x 128!:2 y takes the string x which must describe a verb, and applies the verb so described to y (as a monad).  It is therefore similar to (3 : 'x') y with the restriction that x must describe a one-line verb without assignments.  The advantage of using 128!:2 rather than the method in the next section is that y does not have to be converted to its string representation.

Passing an Executable Sentence: Monad ". and 5!:5

As the ultimate in flexibility, you can pass an entire J sentence as a character string and then execute it with monad ". .  It is executed exactly as if it had been a line of the executing verb (or from the keyboard if that's where ". was entered).  For example:

   ". 'a =. i. 4'
0 1 2 3

The sentence was executed.

   a
0 1 2 3

The assignment was performed.

   ". '+/' , ": a
6

The operand of monad ". must be a string, so before we can take its total we must convert a to a sequence of characters that will have the value of a when executed.  For a large operand, converting to string form adds overhead that might steer you towards using a gerund or 128!:2 instead.

I think C programmers are likely to overlook opportunities to use monad ". because it is so foreign to their experience.  It is equivalent to compiling and executing a code segment as part of the running program--it's just unthinkable.  But in J, it's commonplace.

If you are going to use monad ". you will face the problem of converting your nouns to string form.  Here's the display of a noun: what character string would you execute to produce a noun with that value?

   a
+------------+----------------------------------+
|+---+--+---+|0 2.25 4.5 6.75 9 11.25 13.5 15.75|
||abc|de|fgh||                                  |
|+---+--+---+|                                  |
+------------+----------------------------------+

Fortunately, you don't have to worry about it.  The foreign monad 5!:5 takes as y a boxed name (not a value) and produces a string which when executed has the same value as the variable named by .  So:

   5!:5 <'a'
(<<;._1 ' abc de fgh'),<2.25*i.8

...and if you tell me you came up with the same string, I'm not going to believe you.


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