Guides/Covering a verb
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)>>