Guides/Covering a verb

From J Wiki
Jump to navigation Jump to search

Probing a J utility by covering verbs

Preamble

When studying how a J utility works, for possible modification or extension, it is useful to be able to run the utility with a probe in place to see what x- and y-arg a given verb is called with, plus the returned result.

The attached script: File:Cover.ijs lets you probe a given verb in any locale to achieve this. The script announces its successful loading by offering you a selection of test expressions to re-enter:

   load '~user/cover.ijs'
	TEST: enter:
cover 'pick_z_'		NB. cover a verb (any locale)
datatype 2x		NB. run a verb calling the covered verb
un cover 'pick_z_'	NB. restore the verb to uncovered state
datatype (0 1)		NB. run the restored verb
coverZ			NB. inspect the record
covercall 0		NB. re-run entry 0 in the record
coverxy 0		NB. put x/y-args into globals: x y
cover0''		NB. clear down coverZ

Example: covering pick_z_ in order to debug it

The standard library defines two verbs: datatype and pick in locale 'z'. Verb: datatype works like this:

   datatype 1 0
boolean
   datatype 1 2
integer

Verb pick_z_ is called by datatype_z_ in its last line:

   datatype
3 : 0
n=. 1 2 4 8 16 32 64 128 1024 2048 4096 8192 16384 32768 65536 131072
t=. '/boolean/literal/integer/floating/complex/boxed/extended/rational'
t=. t,'/sparse boolean/sparse literal/sparse integer/sparse floating'
t=. t,'/sparse complex/sparse boxed/symbol/unicode'
(n i. 3!:0 y) pick <;._1 t
)

Suppose we want to debug pick, as it operates inside datatype.

   cover 'pick_z_'		NB. cover a verb (any locale)
covered verb: pick_z_

Now run datatype to see what arguments pick gets called with:

   datatype 2x
┌────────┬─┬───────┬──────────────────────────────────────────────────────────────────────...
│extended│6│pick_z_│┌───────┬───────┬───────┬────────┬───────┬─────┬────────┬────────┬────...
│        │ │       ││boolean│literal│integer│floating│complex│boxed│extended│rational│spar...
│        │ │       │└───────┴───────┴───────┴────────┴───────┴─────┴────────┴────────┴────...
└────────┴─┴───────┴──────────────────────────────────────────────────────────────────────...
extended

   datatype 0 1
┌───────┬─┬───────┬───────────────────────────────────────────────────────────────────────...
│boolean│0│pick_z_│┌───────┬───────┬───────┬────────┬───────┬─────┬────────┬────────┬─────...
│       │ │       ││boolean│literal│integer│floating│complex│boxed│extended│rational│spars...
│       │ │       │└───────┴───────┴───────┴────────┴───────┴─────┴────────┴────────┴─────...
└───────┴─┴───────┴───────────────────────────────────────────────────────────────────────...
boolean

NOTE[1]: J output has been truncated using: 9!:37 (0 90 0 222)

NOTE[2]: Ctrl+R also uses pick, so you might see evidence of additional calls.
This will make the result differ, but will not harm the principle of what follows.

We have now collected sufficient data in coverZ for us to use. So now it's wise to uncover pick before we continue:

   un cover 'pick_z_'
uncovered/restored verb: pick_z_

Now running datatype or pick will collect no more data in coverZ, and emit no more trace output:

   datatype 3.45 6.7		NB. run the restored verb
floating

Look at the data we have collected in coverZ ...

   coverZ
┌─────────────────────────────────────────────────────────────────────────────────────────...
│┌────────┬─┬───────┬─────────────────────────────────────────────────────────────────────...
││extended│6│pick_z_│┌───────┬───────┬───────┬────────┬───────┬─────┬────────┬────────┬───...
││        │ │       ││boolean│literal│integer│floating│complex│boxed│extended│rational│spa...
││        │ │       │└───────┴───────┴───────┴────────┴───────┴─────┴────────┴────────┴───...
│└────────┴─┴───────┴─────────────────────────────────────────────────────────────────────...
├─────────────────────────────────────────────────────────────────────────────────────────...
│┌───────┬─┬───────┬──────────────────────────────────────────────────────────────────────...
││boolean│0│pick_z_│┌───────┬───────┬───────┬────────┬───────┬─────┬────────┬────────┬────...
││       │ │       ││boolean│literal│integer│floating│complex│boxed│extended│rational│spar...
││       │ │       │└───────┴───────┴───────┴────────┴───────┴─────┴────────┴────────┴────...
│└───────┴─┴───────┴──────────────────────────────────────────────────────────────────────...
└─────────────────────────────────────────────────────────────────────────────────────────...

NOTE: coverZ is configured vertically for ease of inspection.

We can re-run pick without datatype, with the arguments it was called-with on any given occasion:

   covercall 1
>>> entry: 1
┌───────┬─┬───────┬───────────────────────────────────────────────────────────────────────...
│boolean│0│pick_z_│┌───────┬───────┬───────┬────────┬───────┬─────┬────────┬────────┬─────...
│       │ │       ││boolean│literal│integer│floating│complex│boxed│extended│rational│spars...
│       │ │       │└───────┴───────┴───────┴────────┴───────┴─────┴────────┴────────┴─────...
└───────┴─┴───────┴───────────────────────────────────────────────────────────────────────...
boolean

   covercall 0
>>> entry: 0
┌────────┬─┬───────┬──────────────────────────────────────────────────────────────────────...
│extended│6│pick_z_│┌───────┬───────┬───────┬────────┬───────┬─────┬────────┬────────┬────...
│        │ │       ││boolean│literal│integer│floating│complex│boxed│extended│rational│spar...
│        │ │       │└───────┴───────┴───────┴────────┴───────┴─────┴────────┴────────┴────...
└────────┴─┴───────┴──────────────────────────────────────────────────────────────────────...
extended

NOTE: x is set to (a:) in coverZ whenever the covered verb is called monadically.
Verb: covercall recognises this and re-runs the verb monadically.

Or we can put the arguments into global nouns x and y, to let us re-enter individual lines of pick using Ctrl+R (supposing it to have more than 1 line):

   coverxy 0
>>> globals x y created from entry: 0
   x
6
   y
┌───────┬───────┬───────┬────────┬───────┬─────┬────────┬────────┬──────────────┬─────────...
│boolean│literal│integer│floating│complex│boxed│extended│rational│sparse boolean│sparse li...
└───────┴───────┴───────┴────────┴───────┴─────┴────────┴────────┴──────────────┴─────────...

To start anew with a fresh coverZ ...

   cover0''		NB. clear down coverZ
initialized: coverZ -in this locale: base

How cover.ijs works

Entering:

   cover 'pick'
covered verb: pick

saves the old definition of pick as a verb: coverSAVED_pick and replaces pick with a new definition:

3 : 0
	NB. template for cover-verb
z=. coverSAVED_pick y
coverZ=: coverZ , <t=. z ; a: ;'pick' ; <y
z [ smtrace t
:
z=. x coverSAVED_pick y
coverZ=: coverZ , <t=. z ; x ; 'pick' ; <y
z [ smtrace t
)

The actual template which governs this cover-verb is a noun:

   coverTP_z_
	NB. template for cover-verb
z=. coverSAVED_NAME y
coverZ=: coverZ , <t=. z ; a: ;'NAME' ; <y
z [ smtrace t
:
z=. x coverSAVED_NAME y
coverZ=: coverZ , <t=. z ; x ; 'NAME' ; <y
z [ smtrace t

It is a LF-separated string -- you can change it to change the definition of subsequent covered verbs.

Note that it also uses a verb: smtrace in place of smoutput ...

   smtrace_z_
3 : '(empty`smoutput @.TRACE)y'

This yields switchable output by setting TRACE_z_ to 0 or 1.

Running:

   cover 'pick'

uses standard library verb: rplc to replace 'NAME' throughout with the name of the target verb, using the resulting string to define the target verb afresh.

Running either expression:

   un cover 'pick'
   0 cover 'pick'

restores pick from coverSAVED_pick and deletes the latter.

Verb: cover has checks to prevent you covering an already covered verb, or uncovering a verb that is not covered.

Any number of distinct verbs can be covered at any given time.

Search nl 3 in all locales for the prefix: coverSAVED_ -- evidence for the existence of covered verbs.


-- Ian Clark <<DateTime(2012-08-12T21:03:09Z)>>