                                   Chapter 12

                             USING J : MISCELLANEOUS


      Let's hear it for APL2 on APL_L
      Reassignment in J
      copula vs branch in J
      Matrix Searching
      J Workspaces
      J: a little exercise


*========================================
# Re: Let's hear it for APL2 on APL_L

+------------------
| Raul Rockwell
| <ROCKWELL.91Jul31235814@socrates.umd.edu>

Well, I should be packing (my plane leaves in 8.5 hours), but this
article looks so tempting I just had to follow-up...

J. P. Benkard:
          Instead of spending $24 for J (that reminds me, I must send
          in my $24), send in $zero for your copy of TRYAPL2.  To
          obtain an order form

I have a copy of TRYAPL2, but I can't use it without a math
coprocessor.

    To plunge right in, one of Bob's remarks was called to my
   attention:: (approx quote)arrays in APL2 are a proper subset of
   arrays in J.  That's a remarkable statement in view of the fact
   that all array *items* in J are scalars while the items in APL2
   arrays can have any rank.

J
   x =. 1;2 3;i.4 5
   $x
3
   #&$2&> x
0 1 2

[[NB. sws.  This just gives a domain error. The following works though
   > #&$ &.> x
0 1 2
]]

APL2
  x  <-  1 (2 3) (4 5 reshape iota 20)
  rho x
3
  rho each rho each x
0 1 2

Of course, the APL2  (0 1 2)  isn't a flat list, but that's just because
I couldn't think of a quick way to make it a flat list.

   In APL2 a nested array is created by writing arbitrary data arrays
   in a row.  2 3:r (3 4 5) 'abXY' (2 3 4:r ?50:r 100) 89 is a motley
   matrix.  (I hope you can figure out what APL symbol :r stands for.)
   Notice, no hand waving, no arbitrarily chosen function, no mystery:
   Nested arrays can be written just like the plain old shallow
   arrays; one simply reshapes a nested vector.

No mystery to someone who knows APL2, sure.  But if you remember that
the original numeric vector shorthand 1 2 3 4 was meant to represent
the vector (1, 2, 3, 4), you might think that the above example should
generate an index error.

So this business about "no hand waving" and "no mystery" seems to me a
mysterious form of handwaving :-)

There's also the little matter of interpreting phrases like
   A B C D E F G

With APL2, a space can represent function application or
data-structure construction.  There are fewer syntactic clues about
what's going on than there are in a variant of APL where space always
indicates function application.

   The important thing is that nested arrays are the starting point.
   Trenchard More has probed the theory, and it works.  We don't need
   :c to create nested arrays.

Err.. well, you could also set up a system where all arrays were
associative, and multi-dimensional arrays are defined by using vectors
as indices.  Larry Wall has probed this theory and it works.

Which is not to say that I like using it.  You have to explicitly
enumerate all the indices (keys) during assignment (unless you prefer
to use the default indices which are the counting numbers: 0 1 2 3 ..)

   The other big push, with a nod or two from Bob, is the rank
   operator.  ...  Unfortunately, for the last axis functions, that
   makes rank of little use.

Eh?

Let X be an array with 27 columns, of arbitrary rank.  Let Y be a
vector with 27 elements.

Using J, you could say   X +"1 Y

    Nested arrays have some other advantages:
     o  structure information is kept with data, whence, as
        Landaeta showed at APL88, it can be inherited by functions
     o  irregular structures are trivial extensions of their uniform
        cousins; potential information is not automatically erased
        with the flattening of results.  In effect, an application of
        rank involves lowering some axes to define cells, performing
        an operation on each of the cells, and then flattening the
        result.  Using nested arrays, this continual packing and
        unpacking is avoided.
        ...

Well, boxed/nested arrays clearly allow you to deal with irregular
structures.  On the other hand, this business about packing/unpacking
to deal with rank is purely an implentation issue -- it is not a
requirement of the design or specification of the rank operator.

[I'm leaving out all the stuff about sorting, I think we've gone over
that enough here.  No?]



*========================================
# Reassignment in J

+------------------
| J Storrs Hall
| <Jan.9.14.25.50.1991.8808@klaatu.rutgers.edu>

In article <1991Jan9.171347.19535@jato.jpl.nasa.gov>, sam@kalessin.jpl.nasa.gov
(Sam Sirlin) writes:

> I've not been able to find a way to do the equivalent of reassignment:

>   A[I] .is X

> in J. Is this really impossible?

Yes and no.  J seems to adhere to some functional language purity
standards to the extent of not allowing destructive modification
of existing data structures.  However the effect can easily be
achieved with dyadic merge:
[[NB. sws.  this is now called ammend ]]

     A =. X I} A

(which is a bit more powerful than reassignment in that it allows you
the option of computing the value without modifying A).


+------------------
| Sam Sirlin
| <1991Jan10.180110.4244@jato.jpl.nasa.gov>

In article <Jan.9.14.25.50.1991.8808@klaatu.rutgers.edu>, josh@klaatu.rutgers.
edu (J Storrs Hall) writes:
..

]>
]>      A =. X I} A
]>
]> (which is a bit more powerful than reassignment in that it allows you
]> the option of computing the value without modifying A).
]>
]> --JoSH

Thanks! I looked at merge, but the description is so obscure that
I didn't see this simple application. I'm just beginning to understand
the boxing necessary in I for indexing. It seems much more complicated
than the indexing function from APL 75 (the model for this?).

This way of changing values is also less powerful than reassignment, since
a duplicate copy of the whole array must be created (unless the language
recognizes the idiom), which could take substantial memory and time. Of
course this mainly affects slow machines with little memory. You
also need more characters for both the indexing ( A[2;3] versus (<2;3){a ).
and the copy of "A."


+------------------
| Bob Bernecky
| <1991Jan10.174836.27468@yrloc.ipsa.reuter.COM>


One way to get the effect of x[i]?k in j is to use merge:

x =. k i{x

That is, merge the elements of k into positions i of x, and
assign the resulting array back into x. It is essentially a
functional form of indexed assignment, witut the side effect
which is present in APL of destroying the original array.

The justification for making catenate work along the first axis
is that you only then need one functional form, comma, rather than
comma, comma-bar, and comma with bracket axis operator. The use
of rank reduces the number of primitives needed, simplifies the
teaching and use of the language, and unifies the treatment of
operations along other axes, by use of the rank adverb.

Generally:

APL           J

x,y           x,"1 y
x?y           x,y
x,[k]y        x,"k y

where k is origin 0.

There is another way to do the merge, but I can't seem to make it work
in my version of J. Bob


+------------------
| Roger Hui
| <1991Jan14.184104.27987@yrloc.ipsa.reuter.COM>


Reply to the following msg re merge.

> From: sam@kalessin.jpl.nasa.gov (Sam Sirlin)
> Newsgroups: comp.lang.apl
> Subject: Re: Reassignment in J
> Message-ID: <1991Jan10.180110.4244@jato.jpl.nasa.gov>
> Date: 10 Jan 91 18:01:10 GMT
>
> Thanks. I looked at merge, but the description is so obscure that
> I didn't see this simple application. I'm just beginning to understand
> the boxing necessary in I for indexing. It seems much more complicated
> than the indexing function from APL 75 (the model for this?).
>
> This way of changing values is also less powerful than reassignment, since
> a duplicate copy of the whole array must be created (unless the language
> recognizes the idom), which could take substantial memory and time. Of
> course this mainly affects slow machines with little memory. You
> also need more characters for both the indexing ( A[2;3] versus (<2;3)}a ).
> and the copy of "A."
>
> --
> Sam Sirlin
> Jet Propulsion Laboratory         sam@kalessin.jpl.nasa.gov

First, regarding the description of merge in the Dictionary, I think
it'd be more accurate to say that it is "terse" rather than "obscure".
(There is a difference.)  In fact, the description of merge is rather
distinguished in that it actually contained some examples!

Second, I don't see how merge is "less powerful than reassignment"
("indexed assignment"), even from the writer's own arguments in the msg.
The implementation may well be smart enough (for all we know) to avoid
making a duplicate of the right argument.  In any case, concern about
possible temporary implementation inefficiencies ought not influence
the language design.

Third, arguing for merge vs. indexed assignment:
(0) merge follows the syntax of the rest of J; indexed assignment
has anomalous syntax.  The benefits of this are twofold: first,
a consistent syntax makes for simple parsing rules; and second,
merge, being an ordinary adverb, can be combined readily with other
constructs in the language.
(1) merge has no side effects, which among other things bodes well
for its implementation on non-sequential machine architectures.
(2) merge permits any verb to be used to select the positions to be
replaced. e.g. replace the diagonal of a matrix.
(3) merge permits replacement of multiple subarrays by an identical
subarray. e.g. replace multiple rows with a single row.

Conclusion: merge is superior to indexed assignment, in syntax and
in semantics.



*========================================
# copula vs branch in J

+------------------
| Robert Bernecky
| <1991May8.153919.5330@yrloc.ipsa.reuter.COM>

MGF and LJD have been discussing how many branches can dance on the head
of one  or more copulas. I see no essential difference between the
APL branch {label}
and the
J  $.=. {label}

in terms of their theoretical wonderfulness, etc. J is perhaps a bit
simpler in this regard.

Howsomeever, I think that J opens a can of worms with regard to data flow
analysis, in allowing a non-item argument to $. It wouldn't be so bad, in
some senses, if the suite were guaranteed to execute, without the possibility
of short-circuiting the execution partway through with another assignment to
$.

But it sure do make things messy for compilers....



*========================================
# Matrix Searching

+------------------
| Robert Bernecky
| <1991Dec4.161942.23875@yrloc.ipsa.reuter.COM>

I just got the 1991/4 issue of the "ZARK APL Tutor News". The first 4
pages of the article are devoted to matrix searching in different
dialects of APL:
    "Write a function... cmiota... which returns a vector of row indices
      that give the locations in A where rows of B first occur."

Functions range in length from one-liners to 21-liners. The shorter ones
are generally limited in capability (small number of columns handled, etc).

I can't resist posting the J solution:

           i.


Want to see it again?  i.



*========================================
# J Workspaces

+------------------
| Roger Hui
| <1991Feb8.145117.25759@yrloc.ipsa.reuter.COM>

Workspaces are now available. (See "status.doc" to see whether your
J version has workspaces.) The current internal representation follows
WSIS (workspace interchange standard), meaning (a) except for contents
of literal arrays, only ASCII 32-126 occurs in a workspace; and
(b) it MAY be possible to transfer data arrays between J and other
APL systems.

The relevant functions are as follows.  All function ranks are zero.

 x  2!:0  y      WS Name Class
    2!:1  y      WS Name List
[x] 2!:2  y      WS Save
[x] 2!:3  y      WS PSave
[x] 2!:4  y      WS Copy
[x] 2!:5  y      WS PCopy
 x  2!:55 y      WS Erase

B
    5!:3  y      String (WSIS) Representation



*========================================
# J: a little exercise

+------------------
| Jonathan Burns
| <1991Sep22.164639.21581@latcs1.lat.oz.au>

I've been playing with J 2.7 for PC, looking for pointers on the array
syntax to build for a mini-language of my own. I like it very much, but
it has its difficulties too, and at this point I'd like to share my
experiences...

What I wanted was to set up a family of point transformations: translations,
rotations, etc.  These functions would be parametrized, as in

        x = 3 4 5
        v = 2 2 2
        Translate(v) x = 5 6 7

Ideally,

(1) A function could be packaged with its inverse, and I could do things
like
        ~( Trans(u) Trans(v) ) x
and have that give the same result as
        ~Trans(v) ~Trans(u) x

(2) Either the x or v argument could be early or late evaluated.

(3) I could easily modify my functions for one-x-many-v, one-v-many-x,
one-v-for-each-x, and each-x-with-each-v.

To summarize, I can do each of these things, but getting them all at
once brings out some problems. And

(4) There should be some way to abstract both the operation (Trans) and
the arguments, to build a family of similar transformation functions.

seems very awkward indeed.



What we need basically is addition, modified for rank 1:

        +"1

This immediately gives the first three cases of (3), and then we can
have
        +"1/
for tables.


To compound transforms,

        v1 +"1 v2 +"1 x

but what looks nicer is

        tr =. +"1

        tr&v1 tr&v2 x

or
        trv1 =. +"1 & v1
        trv2 =. +"1 & v2

        trv1 trv2 x

(N.b. I was too greedy here, and what I wound up with was the equivalent
of
        x +"1 v2 +"1 v1

which is correct for this case, but not necessarily for not-commutative
functions.)

To make these invertible,

        trv =. (+"1 & v) :. (-"1 & v)

Then with x = 3 4 5, v = 2 2 2,

        trv^:1 x  =  5 6 7
        trv^:2 x  =  7 8 9

        trv^:(0 1) x   = 3 4 5
                         5 6 7

and even

        trv^:_1 x  =  1 2 3

Better still,

        v1 =. 2 2 2
        v2 =. 10 10 10

        trv1 =. (+"1 & v1) :. (-"1 & v1)
        trv2 =. (+"1 & v2) :. (-"1 & v2)

        trv1^:_1 trv2^:_1 x  =  _9 _8 _7

        (trv2 & trv1)^:_1 x  =  _9 _8 _7

I tried

        tr =. (+"1) :. (-"1)
        trv =. tr & v

        trv^:n x  gives the expected results for non-negative n, but
gets a domain error for negative. Evidently inverses can only be evoked
for verbs with :. at the top level (clearly shown in the box display of
trv).

That seems a reasonable policy, because the & conjunction has to do
service two ways:

        (+"1 & v) x  =  x +"1 v
        (v & +"1) x  =  v +"1 x

However, when I thought it over, I could see no contradiction in having
an inverse defined for a dyadic, and having it automatically extended
to right and left:

        (-"1 & v) x  =  x -"1 v
        (v & -"1) x  =  v -"1 x

So these are different values, so what? Just means you get two monadic
inverses for the price of one.  Comments?


Getting late evaluation of the noun operands is not as easy as it looks.

Trying for late v, with v = 3 3 3 at define time,

        (+"1 & ". 'v')  evaluates early, to 3 3 3

        (+"1 & (". 'v')) at least gives a verb, the equivalent of (+"1 & 3 3 3)

WHY are these different?

After a good deal of trial and error, I got it to work with a hook:

        ((+"1 ".) & 'v')  evaluates to a boxed verb with a v in it.

 take this to indicate that, to keep the ". from executing at define-time,
you have to drag it inside a verb which will not itself be executed until
it gets (in this case) a left operand. I feel there ought to be a cleaner
way.

Our full transform verb is then:

        trv =. ((+"1 ".) & 'v') :. ((-"1 ".) & 'v')

This has all the nice function-power properties. We need a similar verb
with +"1/ for tables, though.

Trying to build an expression on this basis which also evaluates x late
defeats me. I just can't keep ". from evaluating early. I gather the
point is moot, since verbs will be late-evaluated by default in Version 3.2
anyway.


All right then, there's our trv. Now suppose I want trv1 and trv2.
Do I have to type their definitions literally, or is there some way
to abstract those 'v's, and substitute 'v1', 'v2'?

Apparently not. There seem to be two approaches: (1) defining a monadic
verb with :: and some string-handling (2) doing surgery on the box form
of trv.

[[NB. sws.  :: is now just : ]]

I'm not enamoured of these :: text definitions. I have to say that if
this is going to be the general means of function definition, then we
need something closer to a macro facility. Otherwise one gets into the
following kind of unpleasantness:

        trvpattern =. '((+"1 ".) & ';  ''; ') :. ((-"1 ".) & '; ''; >'))'

        mtrv1 =. (> 0{ trvpattern), 'v1', (> 2{ trvpattern), 'v1',
                (> 4{ trvpattern)

        trv1 =. mtrv1 :: ''

And where is the legendary brevity of the APL family in this?

I could hack that, in principle, but the real problem with this approach
is that the resulting expression does not have :. holding the top-level
functions together, so we lose the inversion ability of ^:_1 again. If
we want it back, we have to build up something like

        mtrv1fwd::'' :. mtrv1bwd::''  where each of the expressions
referred to is built up as above.

Approach (2); well, it looks as if it can be done, with enough opening
and closing of boxes. But I'm not certain whether the boxes we see in
these verb diagrams are the same ones we use when forming boxed expressions.


The bottom line is, J does not yet seem to have the control over its own
parts of speech that every interpretive language requires. I'm not
impatient, the care that has gone into exploiting 22 typewriter keys
is perfectly awesome. Better this thing should be done right than soon.

In my tentative opinion, the appropriate way to go is neither to push
character arrays in non-array-like directions, nor to build a calculus
of box-nests. It would be more in character to build up the repertoire
of adverbs and conjunctions: powerful abstractions for building functions
out of functions.

Also, it's plain that J really has two components, more or less modular:
one is the array processing, the other is the evaluation algorithm. The
former is hellishly powerful, a good step up from APL. The latter is
elegant, but we need a better understanding of when and how the various
parts of speech come into play.

Roger Hui has my profound admiration.


+------------------
| Raul Rockwell
| <ROCKWELL.91Sep22151717@socrates.umd.edu>

Jonathan Burns:
[] However, when I thought it over, I could see no contradiction in
[] having an inverse defined for a dyadic, and having it automatically
[] extended to right and left:

[]         (-"1 & v) x  =  x -"1 v
[]         (v & -"1) x  =  v -"1 x

[] So these are different values, so what? Just means you get two
[] monadic inverses for the price of one.  Comments?

It is generally true that if both
   y = (x&G) (x&g) y
   x = (G&y) (g&y) x
then G might be considered an 'dyadic inverse' of g.  Obviously, this
is not considered in the current versions of J.

[] Getting late evaluation of the noun operands is not as easy as it looks.

Try
   late =. '".@(x.&[)' : 1

For example,
   'name' late
is a monadic function which always returns the current value of the
pronoun 'name'.

[] Trying for late v, with v = 3 3 3 at define time,
[]    (+"1 & ". 'v')  evaluates early, to 3 3 3
[]    (+"1 & (". 'v')) at least gives a verb, the equivalent of (+"1 & 3 3 3)
[] WHY are these different?

The first is equivalent to
     ((+"1 & ".) 'v')
since conjunctions parse left to right.

   ...
[] Our full transform verb is then:
[]         trv =. ((+"1 ".) & 'v') :. ((-"1 ".) & 'v')
[] This has all the nice function-power properties. We need a similar
[] verb with +"1/ for tables, though.

I'm not sure I understand what the semantics of [+"1/] are supposed to
be, such that you can invert it.  Inverse doesn't have a functional
meaning except with monads.

[] Trying to build an expression on this basis which also evaluates x
[] late defeats me. I just can't keep ". from evaluating early. I
[] gather the point is moot, since verbs will be late-evaluated by
[] default in Version 3.2 anyway.

Again, I'm not sure what is being said here.  Note that in 3.2 nouns
are still evaluated greedily.  At a guess, though, you want something
like.

   example =. 'x f y' late

which is a monadic verb which ignores its argument, but resolves the
binding of 'x', 'f', and 'y' at the time that 'example' is passed an
argument.

[] All right then, there's our trv. Now suppose I want trv1 and trv2.
[] Do I have to type their definitions literally, or is there some way
[] to abstract those 'v's, and substitute 'v1', 'v2'?

The problem here is still that inverse doesn't understand about the
obverse of a dyadic function.  This requires either a language change
or that you define your own version of 'inverse'.  Other than that,
all you're doing is defining new names for addition.

[] The bottom line is, J does not yet seem to have the control over
[] its own parts of speech that every interpretive language requires.
[] I'm not impatient, the care that has gone into exploiting 22
[] typewriter keys is perfectly awesome. Better this thing should be
[] done right than soon.

Well, no, the real problem here is that the inverse operator is not
general enough.  [Though I agree about the care which has gone into
the language design.]


+------------------
| Raul Rockwell
| <ROCKWELL.91Sep22152034@socrates.umd.edu>

I wrote:
   It is generally true that if both
      y = (x&G) (x&g) y
      x = (G&y) (g&y) x
   then G might be considered an 'dyadic inverse' of g.  Obviously, this
   is not considered in the current versions of J.

However, on consideration of the definition of CHAIN (f^:n, used
dyadically), I think it would be better to make the conditions:
   y = (G&x) (x&g) y
   x = (y&G) (g&y) x
for G to be considered a 'dyadic inverse' of g.


+------------------
| Raul Rockwell
| <ROCKWELL.91Sep22225810@socrates.umd.edu>

Let's see if I can complete my idea, this time...

I wrote:
   I think it would be better to make the conditions:
      y = (G&x) (x&g) y
      x = (y&G) (g&y) x
   for G to be considered a 'dyadic inverse' of g.

Note that if (g =. +) there is no function G which is a complete
inverse of g.  If (G =. -), then the first condition holds, but the
second condition does not.  If (G =. -~) then the second condition
holds but the first does not.  It would be a shame to be limited to an
implementation of dyadic inverse which cannot deal with (+).

So, perhaps the best thing to do would be to allow a gerundial left
argument to obverse.  The structure of this argument would be vaugely
patterned after the right argument to rank (").  So, for example, a
person might define:

   minus =. - ` (-~) ` + :. -

so that, for example,
   inv =. ^: _1

  minus inv
+-----+--+-+
|minus|:.|-|
+-----+--+-+
   minus inv minus 4
4

   5&minus inv
+-----------+--+-----------+
|+-+-+-----+|:.|+-----+-+-+|
||5|&|minus||  ||+-+-+|&|5||
|+-+-+-----+|  |||-|~|| | ||
|           |  ||+-+-+| | ||
|           |  |+-----+-+-+|
+-----------+--+-----------+
   5&minus inv  5&minus 4
4

   minus&5 inv
+-----------+--+-------+
|+-----+-+-+|:.|+-+-+-+|
||minus|&|5||  ||5|&|+||
|+-----+-+-+|  |+-+-+-+|
+-----------+--+-------+
   minus&5 inv  minus&5 ]4
4

Note that there is a little ambiguity here regarding the monadic and
dyadic case.  For example (minus inv inv & 5) may be treated rudely.
Maybe if minus were defined
   minus =. minus ` (minus~) ` + :. -
one could pull simplification stunts like
   minus inv
+-----+
|minus|
+-----+

I dunno, this may be getting too messy.

Comments?


+------------------
| Jonathan Burns
| <1991Sep23.162253.16110@latcs1.lat.oz.au>


In article <ROCKWELL.91Sep22151717@socrates.umd.edu>
 rockwell@socrates.umd.edu (Raul Rockwell) writes:

> [] Getting late evaluation of the noun operands is not as easy as it looks.
>
> Try
>    late =. '".@(x.&[)' : 1
>
> For example,
>    'name' late
> is a monadic function which always returns the current value of the
> pronoun 'name'.

That's what I was after. I'm still at 2.7, with [ and ` unimplemented,
but no rush.

One thing I will say, though: there's a word for languages where you have
to work out something ingenious to get as intuitive a mechanism as call-
time-binding, and the word is _low-level_ :-)

> [] Trying for late v, with v = 3 3 3 at define time,
> []    (+"1 & ". 'v')  evaluates early, to 3 3 3
> []    (+"1 & (". 'v')) at least gives a verb, the equivalent of (+"1 & 3 3 3)
> [] WHY are these different?
>
> The first is equivalent to
>      ((+"1 & ".) 'v')
> since conjunctions parse left to right.

Ah, of course. Thanks.

> I'm not sure I understand what the semantics of [+"1/] are supposed to
> be, such that you can invert it.  Inverse doesn't have a functional
> meaning except with monads.

Sorry, I wan't thinking straight there. Inverse applies where you
perform one operation on however many items, as in (+"1 & v) x or
(v & +"1) x, where v is of rank 1. It doesn't make sense for cartesian
products.

[] Trying to build an expression on this basis which also evaluates x
[] late defeats me. I just can't keep ". from evaluating early. I
[] gather the point is moot, since verbs will be late-evaluated by
[] default in Version 3.2 anyway.

> Again, I'm not sure what is being said here.  Note that in 3.2 nouns
> are still evaluated greedily.  At a guess, though, you want something
> like.
>
>    example =. 'x f y' late
>
> which is a monadic verb which ignores its argument, but resolves the
> binding of 'x', 'f', and 'y' at the time that 'example' is passed an
> argument.

That's right.

> [] All right then, there's our trv. Now suppose I want trv1 and trv2.
> [] Do I have to type their definitions literally, or is there some way
> [] to abstract those 'v's, and substitute 'v1', 'v2'?
>
> The problem here is still that inverse doesn't understand about the
> obverse of a dyadic function.  This requires either a language change
> or that you define your own version of 'inverse'.  Other than that,
> all you're doing is defining new names for addition.

Not exactly. The problem is that we've made a machine for adding v
to all rank-1 cells of its operand, and now we want a machine for making
such machines. It seemed to me the two ways to do this were (1) to
write a string s(v) such that ''::s(v) is the machine we want, and
then a string-builder which substitutes whatever we want for v;
or (2) build something that reaches inside a verb and substitutes for
an internal constant or an internal verb.

(A fair retort would be "Go build your adders in Lisp". But I'm still
getting to grips with what J can do and can't, and being surprised.)

In this case, yes, it was unreasonable to couple two dyadics with the
:. conjunction, and expecting to get monadic inverses when I specialized
them to right or left with &. But the question of how much we can
abstract from a verb definition remains.

Later....

> I wrote:
>  It is generally true that if both
>     y = (x&G) (x&g) y
>     x = (G&y) (g&y) x
>  then G might be considered an 'dyadic inverse' of g.  Obviously, this
>  is not considered in the current versions of J.

> However, on consideration of the definition of CHAIN (f^:n, used
> dyadically), I think it would be better to make the conditions:
>   y = (G&x) (x&g) y
>   x = (y&G) (g&y) x
> for G to be considered a 'dyadic inverse' of g.

Nice try, but I've learnt my lesson. 'Dyadic inverse' is trying to get
inversions from positional information, and the inversions usually won't
be there. Consider:

        Function        Inverse

        + & x           + & (-x)        Addition on right
        - & x           - & (-x)        Subtraction on right

        x & +           (-x) & +        Addition on left
        x & -           x & -           Negation followed by addition on left

The inverse of + just isn't - in any consistent sense. Likewise, the
inverse of left matrix multiplication is just left multiplication again,


by the inverse matrix.

Thanks very much for your incisive comments.


+------------------
| Raul Rockwell
| <ROCKWELL.91Sep23205457@socrates.umd.edu>

Raul Rockwell:
   >    late =. '".@(x.&[)' : 1

Jonathan Burns:
[] That's what I was after. I'm still at 2.7, with [ and `
[] unimplemented, but no rush.

I think that for 2.7 you'd say
   late =. '".@(x.&}:)' : 1

[] The problem is that we've made a machine for adding v to all rank-1
[] cells of its operand, and now we want a machine for making such
[] machines.

Try:
   mm=. machine_maker=. '(x. late  + ]) :. ([ - x. late) "1' : 1

[I think I got the argument order for :. right this time.  Function on
the left, inverse on the right.]

For 2.7, that probably should be:
   mm=. machine_maker=. '(x. late  + {:) :. (}: - x. late) "1' : 1

For example,
   f =. 'v' mm
   v =. 4
   f i. 3
4 5 6
   v =. i. 3
   f i. 3
0 2 4

[] It seemed to me the two ways to do this were (1) to write a string
[] s(v) such that ''::s(v) is the machine we want, and then a
[] string-builder which substitutes whatever we want for v; or (2)
[] build something that reaches inside a verb and substitutes for an
[] internal constant or an internal verb.

Well, you could do it either of those ways, but the extra indirection
adds complexity.  Then again, as long as v is supposed to be a global
var, you could write
   s =. 'y. + '&,

This isn't going to give you an invertible function, but if you define
   g =. (s 'v') : ''
then you could say
   g i. 3 3
0 2  4
3 5  7
6 8 10

[Though this might give an error under 2.7.  I don't remember when
vector extension began working for scalar verbs.]

[] But the question of how much we can abstract from a verb definition
[] remains.

Well, as a general rule, you're going to want to take a step back and
write the abstraction yourself.  There are many forms of abstraction.


 