User:Pascal Jasmin/The power of one line statements

From J Wiki
Jump to navigation Jump to search

Statements that can be made in one line are the essence of reusable code. Long before you understood the importance of re-use, you copy pasted edited code. Even that primitive form of re-use is made simpler by a one-line source. In addition to quick selection, editing of the copy directly below the original provides an easy way to spot the changes.

One-liners are a clear strength of J. Many people stay away from J or even stay away from one-liners in J with the accusation that one-liners are less readable. Consider:

multiply =: *

3 multiply 4

NB. in some other language
multiply(3,4)

Every language prefers 3 * 4, though all can express the statement in one line either way. In the case of multiply, neither is more or less readable, but the other language version is more annoying to write, requiring "(,)" additional characters.

If we replace the function multiply with getNuclearFusionTorusParameters, then while we may have a reusable functions, it will be unreadable in any language if the reader barely understands torus, fusion, and what returned parameters could mean. Any reusable code is by definition less readable than repeated inline code is a corollary of a readability accusation against J.

Optimal reuse is achieved through the ability to insert code with a single cursor positioning

If you can place your cursor within a line, or at worse, select a continuous portion of a line, and then press keys to modify a reused expression, then that expression is optimally reusable. The more cursor repositionings are required, the less reusable the expression.

The power of no operation
x ] y NB. returns y.  x&] is a no-op

applyif =: ^:
u applyif v NB. returns y or u y.  v y creates a no-op if 0=v y

The above 2 expressions are well-known J patterns that can be inserted within a line, with the possibility or certainty of not modifying what is to the right of it that will be made available to the left of the expression. The nice thing about the u^: pattern is that if u y is the same shape as y, then other code will likely be ok. The problem with if. do. else. structures is that they double the chances of screwing up the expression result in that both sides of the else statement have to be correct. It's much less work to write an expression where the else side is no-op.

Side-effect

No-op's are useful primarily for allowing side effects. The basic side effect no-op available in most languages (though surprisingly screwed-up in many, too) is to print an expression/result to console or logfile. J's basic included function has a design flaw here:

   smoutput
0 0 $ 1!:2&2

   pD
1!:2&2

smoutput reshapes y, for no apparent useful purpose, and messes up cursor alignment in console. pD correctly produces the desired side-effect with a total no-op to y. Coding is easier when an optional token's presence or non-presence has predictably simplest possible effects.

Composability adverbs

noP =: 1 : ']'

   2 + noP 3  NB. noP is adverb that effectively cancels the entire verb phrase that it applies to.
3

   sfX =: 1 : '][u'
   assign =: 4 : '(x) =: y'  NB. assign is a no-op on its own.  returns y
   'c' assign 5
5

   2 ('c' assign *) sfX 5   NB. assign to c 2 * y, as side effect, but return y
5
   c
10
   2 ('c' assign *) sfX noP 6  NB. noP adverb means do nothing at all except return y
6

sfXA =: 2 : '][n assign u'   NB. the side effect is assigning the result of u to the variable in n.  Expression still returns y.

   2 * sfXA 'c'  7
7
   c
14

The usefulness of these functions is often related to debugging vs. production environments. No-op operations can be redefined at startup time to respond to the environment. Regarding the side effects functions, you can design any function that returns anything, and applying the sfX or sfXA adverb use them only for their side-effect purposes. All of these functions meet the criteria of single cursor positioning for inserting or removing them.

A more advanced version of sfXA could append to a logfile. The logfile parameter (v) can either be a variable set at startup, or the entire expression can be bound with the specific logfile as an adverb.

A more involved example:

sfXC2 =: 2 : '][ v@:u' NB. conjunction where u is any function with any result, and v determines what to do with that result (what side effect), while still returning original y
sfXC =: 2 : '][ (v@:u)^:(0=]-:u)'  NB. same, but only applies side effect if u y modifies y.

   lrA =: 1 : '5!:5 < ''u'''

warnifnot =: (1 : ' (''WARNING: not '' , u lrA , ,~&('' ''&,)&":)^:(0=u@:])') (sfXC pD)  NB. double adverb. 2nd (right) says what side effect to produce: conditional print console
                                            NB. first (left) prints 'WARNING:' together with parameter info and optional text.

   'even' (0=2&|) warnifnot 3
WARNING: not 0 = 2&| 3 even
3                                NB. still returns 3 no matter what the side effect.
   'even' (0=2&|) warnifnot 2
2                                NB. no warning output.
   warnifnot
1 : ' (''WARNING: not '' , u lrA , ,~&('' ''&,)&":)^:(0=u@:])'(sfXC pD)

Unfortunately (a c) is not an allowable definition (though see my CONJ modifier to enable this not quite as cleanly as native support), and so we can't define warnifnot with a direct open-ended side-effect to apply (deciding where to log/print warning), but because pD is not fixed, it can be bound to a verb whose definition will be modified at any time (effectively auto-switching among logs/consoles), so one management technique:

pDS =: pD
pDL =: printToLog
warnifnot =: (1 : ' (''WARNING: not '' , u lrA , ,~&('' ''&,)&":)^:(0=u@:])') (sfXC pDS)

sets warnifnot to direct its side-effects to the console by default, but pDS can be redefined at any time in order to direct side-effects somewhere else.

This use of adverbs may or may not be impressive. Perhaps warnings feel like a toy, but it is a useful documentation feature. While J is single threaded, it is possible to structure/schedule "task columns", where you would prefer that one bad column doesn't halt the entire program. There are many variations for applications.

There is also a neat J feature here with its adverbs/conjunctions that may be underused. Separating a functional pure call and turning that call into a side effect where neither side cares about the other. More involved examples would be markup code that can be interpreted by multiple side-effect presenters such as html, opengl, or wd/jqt.

An alternative that is more typically used would be "side-effectF F y". The difference is that side-effect F must ensure to return y after it has completed in order to be equivalent. Its a bit easier to design and test side-effect F if it simply returns the intended side-effect, and through adverbs the side-effect is isolated and applied. The adverb approach ensures that if the side-effect is wrong (short of halting the program) it doesn't affect the continued execution of the line/program.

In otherlanguage, calling warnifnot would look like:

warnifnot('even', lambda(0=2&|). sideeffecttoapplyfunc, func(value))

The parentheses may not look like a big deal, but it's enough to be unable to paste or cut in order to add or delete the warning code. In the case of the above J code "'even' (0=2&|) warnifnot" can be added or removed without affecting the rest of the sentence/statement.