defns ← {names←0 {list←0 {space←⎕this}}} ##.defs namelist   ⍝ Extended ]defs.

[defs] implements an extended version of the ]defs user command. It takes a vec-
tor [namelist]  as  right  argument  and returns a display of the definitions to
which the names refer.

Left argument item [names] specifies whether names or values should be used for
derived function components

    0: -names=off   show values (default)
    1: -names=fns   show names of functions and operators where available
    2: -names=all   include names of arrays where available

        succ ← (one←1)∘(plus←+)     ⍝ definitions

        0 defs 'succ'               ⍝ showing component values
    succ ← 1∘+

        1 defs 'succ'               ⍝ showing names of functions
    succ ← 1∘plus

        2 defs 'succ'               ⍝ showing names of functions and arrays
    succ ← one∘plus

[list] determines the listing order:

    0:              alphabetic listing (default)
    1: -topdown     top-down listing
   ¯1: -refs        reference/dependency order (Hsu)

        2 0 defs ''                 ⍝ alphabetical listing
     one ← 1
    plus ← +
    succ ← one∘plus

        2 1 defs 'succ'             ⍝ top-down calling tree for "succ"
    succ ← one∘plus
      one ← 1
      plus ← +

        2 ¯1 defs 'one'             ⍝ dependency (refs) tree for "one"
    one ← 1
      succ ← one∘plus

The final item of the left argument is the name[space] in which to find definit-
ions.

NB:  The appearance of a name in the result indicates only that [defs] has found
a definition that matches the value of this component. If we withdraw the defin-
ition this will no longer be the case and the name will disappear from the disp-
lay:

    mean ← (sum←+⌿)÷≢               ⍝ more definitions

    1 defs 'mean'                   ⍝ "mean" with name for "sum"
mean ← sum÷≢

    ⎕ex'sum'                        ⍝ remove definition of "sum"

    1 defs 'mean'                   ⍝ "sum" disappears from ]defs display
mean ← +⌿÷≢

Likewise, _changes_ to the definition of "sum" change the display of "mean" but
do not alter its behaviour.

    sum ← +⌿            ⍝ reinstate definition of "sum"

    1 defs 'mean'       ⍝ display of "mean" incorporates the definition
mean ← sum÷≢

    mean 1 2 3 4        ⍝ "mean" gives expected result
2.5
    sum ← ?             ⍝ change definition of "sum"

    1 defs 'mean'       ⍝ display of "mean" no longer references "sum"
mean ← +⌿÷≢

    mean 1 2 3 4        ⍝ behaviour of "mean" is _unchanged_
2.5

Such considerations apart, defs can be helpful when developing derived functions
that reference other derived functions.  For  large  "trees" of definitions, the
]boxing and ]defs value displays soon become unwieldy:

    f ← +                      ⍝ tree of forks ...
    g ← f f f
    h ← g g g
    i ← h h h
    j ← i i i

    ]boxing -trains=def
Was -trains=def

    j                           ⍝ boxing displays _values_
(((+++)(+++)+++)((+++)(+++)+++)(+++)(+++)+++)(((+++)(+++)+++)((+++)(+++)+++)(+++)(+++)+++)((+++)(+++)+++)((+++)(+++)+++)(+++)(+++)+++

    0 defs 'j'                  ⍝ display with values
j ← (((+++)(+++)+++)((+++)(+++)+++)(+++)(+++)+++)(((+++)(+++)+++)((+++)(+++)+++)(+++)(+++)+++)((+++)(+++)+++)((+++)(+++)+++)(+++)(+++)+++

    1 defs 'j'                  ⍝ display using names
j ← i i i

Remember that definitions can be corrected simply by changing  and  resubmitting
relevant lines of the ]defs display.  For a demonstration of this technique, see
Sudoku-solver video: https://www.youtube.com/watch?v=DmT80OseAGs

        2 defs ''               ⍝ some definitions with names
     fold ← ⌿
     mean ← sum÷num
      num ← ≢
      one ← 1
     plus ← +
     pred ← -∘one
     same ← succ∘pred
     succ ← one∘plus
      sum ← plus fold

Using the ¯1 option to display in referenced-by order makes it easier  to  main-
tain derived functions  by resubmitting dependent definitions following a change
above them in the list.

        2 ¯1 defs ''            ⍝ all definitions in dependent order
    fold ← ⌿
    num ← ≢
    one ← 1
    plus ← +
    pred ← -∘one
    succ ← one∘plus
      same ← succ∘pred
    sum ← plus fold
      mean ← sum÷num

After correcting one definition, "fold" say, in the above display, we  must  re-
submit dependent definition "sum" and definition "mean",  which depends on that.
Because of the display order, the three definitions can be  resubmitted  in  one
interaction. Remember that dependent definitions such as "sum" and "mean"  above
must be reassigned, whether changed or not, in order to incorporate  changes  in
their  dependents.  To  mark  a definition for resubmission without changing it,
just add a blank to the end of the line.

        1 ¯1 defs 'plus' 'one'      ⍝ definitions dependent on "plus" and "one"
    plus ← +
      sum ← plus fold
        mean ← sum÷num
    one ← 1
      pred ← -∘one
      succ ← one∘plus
        same ← succ∘pred

Finally, defs accepts an assignment expression, which automatically updates any
dependent definitions.

        defs 'fold←/'           ⍝ change defn and any of its dependents

Technical notes:

[defs]' primary data type "NKTDs" is a vector of [Name Kind Defn Tree]-tuples,
where:

    Name: character vector.                                    'mean'
    Kind: 2:array 3:function 4:operator.                        3
    Tree: vector of kinds of derived function components.      (3 4)3 3
    Defn: nested ⎕CR of the derived function.                  '+⌿' '÷' '≢'

Inner  function  [nabs]  substitutes  nested sub-expressions with names wherever
possible, using its own inner functions:

    [names] takes a vector of kinds and returns an NKTDs vector for all names in
    the target namespace. Definitions are reduced by substituting names for  all
    sub-expressions identical to those names' referent values.  [names]  applies
    [subs] under reduction to the NKTDs vector, whose  items  have  been  sorted
    into order of increasing size.  This ensures that the largest possible subs-
    titution is made at each step of the reduction.  [names]  is  an  O(×/d s*2)
    function,  where d is the number of definitions,  and s the mean definition-
    tree size.

    (muse:

        A common pattern in procedural programming is to update some variable X,
        using function F, if predicate P is true:

            X ← I
            :If P
                X ← F X
            :EndIf

        where X::T ("X is of type T"), F::T←∇T and P::Bool, for some type T.

        The equivalent functional coding uses the power operator, which can bind
        the predicate either at application time:

            X ← F(⍣P)I

        or, as in defs's inner function [names], at F-definition time:

            F ← {...}⍣P
            X ← F I

        [defs] uses derived operators R and T for the conditional application of
        a left operand function, depending on whether the listing option is  for
        refs (_l=¯1) or top-down (_l=1):

            R←⍣(_l≡¯1) ⋄ T←⍣(_l≡1)              ⍝ if -refs or if -topdown

        R and T are used within inner function [tree]:

            accm←{⍺,(⍺ new ⍵)/T⊂⍵}              ⍝ collected lines and tab values
            subs←{(⍺ new ⍵)/T⊃next/⍵}           ⍝ unvisited edges
            new←{~(⊃⍵)∊⊃¨⍺}T                    ⍝ node unvisited?

        and for the conditional application of [dervs], a filter for those def-
        initions that are derived functions:

            trim↑⍵ tree dervs R defns           ⍝ calling or called-by tree
    )                   ¯¯¯¯¯¯¯

    [subs] takes a vector of NKTDs processed so far as an accumulating left arg-
    ument and the next definition to be examined on the right.  Function →in← is
    called to search the accumulated definitions for any  occurrences of the new
    definition and any _hits_ replaced with its name.

    [uniq] removes duplicate definitions from the NKTDs vector, otherwise [defs]
    would not be able to decide which name to substitute.  For example, ⌊ in the
    following definition of indx has two aliases "floor" and "min":

            floor ← ⌊
            min ← ⌊
            indx ← ⊢⍳⌊/
            defs'indx'      ⍝ neither name substituted for ⌊
        ⊢⍳⌊/
            )erase floor
            defs'indx'      ⍝ substitution occurs as ⌊ now has a unique name
        ⊢⍳min/

    [nktds] returns an NKTDs vector for all one-line definitions in the target
    namespace.

    [prep] pre-processes nested (⎕CR) definitions by:
        disclosing 1-vectors, which arise from named primitive functions, and
        removing the "name←" tag from the start of any named dfns.

    [ktree] takes a definition (nested ⎕CR) and attempts  to  return  the  equi-
    valent nested vector of kinds.   This involves a certain amount of guesswork
    and is a weak component of the whole enterprise.

    (muse:                                                                  <V>

        It is often difficult to deduce the kinds of derived function components
        from their ⎕CR forms. The following distinct trains have identical ⎕CRs:

            f←,,, ⋄ g←',',, ⋄ ⎕cr¨'fg'
        ┌───┬───┐
        │,,,│,,,│
        └───┴───┘

        [defs] could be much improved by replacing [ktree] with an interpreter-
        supplied mechanism (1∘⎕NC, say) to provide train-component kinds:

            mean ← +⌿÷≢ ⋄ 1 ⎕NC 'mean'      ⍝ kind-tree
        ┌───┬─┬─┐
        │3 4│3│3│
        └───┴─┴─┘

            1 ⎕NC¨ 'fg'                     ⍝ (f g from above)
        ┌─────┬─────┐
        │3 3 3│2 3 3│
        └─────┴─────┘
    )

    [curry] adjusts the kind trees and definitions to  accomodate  right-operand
    currying.  This is where a dyadic operator binds its right operand to derive
    a monadic operator. Dyalog allows us to name such derivations:

        inv ← ⍣¯1       ⍝ derived monadic inverse operator

    If such an operator is subsequently bound with a left operand:

        enc ← ⊥ inv     ⍝ derived function

    the ⎕cr of the resulting function is a triple (⊥ ⍣ ¯1). In order for defs to
    be able to substitute name "inv" for ⍣¯1, the representation of the function
    is converted to the pairs: (⊥ ⍣ ¯1) → (⊥(⍣ ¯1)).

[expr] takes the reduced expression tree (D) together with  its  kind-tree  (T),
and returns a parenthesised, linear form  of  the  expression.  [expr] traverses
(T D) pairs  for each reduced definition, joining sub-expressions with appropri-
ate parentheses.

[crep] takes an array value and returns a character vector representing an expr-
ession that evaluates to that value.  [crep] requires library utility  function:
⎕SE.Dyalog.Utils.repObj.

[join] concatenates its argument expressions with a blank between adjacent names
or numbers.

[trim] removes outer blank columns from its character matrix argument.

[adj]  returns a pair:  the rightmost item of its left argument and the leftmost
item of its right argument: adj'hello' 'world' → 'ow'.   [adj] is used by [expr]
to check the binding of adjancent kinds,  and by [join]  to determine whether to
blank-separate adjacent tokens such as names and numbers.

[name] extracts the name part of a definition: name' this ← that' → 'this'.

[redef] implements the re-definition option:  ]defs name ← defn.

[tree] implements the top-down and refs tree displays. The two cases are similar
enough to warrant sharing the majority of the code. Differences are parameteris-
ed using operators T and R, which conditionally apply their  operand  functions,
depending on whether _l=1 (topdown) or ¯1 (refs).   The function calls →externs←
so that local names in dfns are not counted as external references.

Requires: →externs← →in←

Examples:

     avg ← (sum←(plus←+)(fold←⌿)) ÷ (num←≢)         ⍝ some definitions ...
    davg ← {(sum÷num)⍵}
     dup ← {⍵ ⍵}
    each ← {⍺←⊢ ⋄ ⍺ ⍺⍺¨⍵}
    dups ← dup each
    same ← (succ←one∘plus)∘(pred←-∘(one←1))
   isnum ← ∊∘(digs←'0123456789')
   twice ← {⍺⍺ ⍺⍺ ⍵}
   greet ← 'hello'twice
    that ← {this ⍵}
    this ← {that ⍵}

⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ Listing with names

    1 defs ''                       ⍝ all defns
  avg ← sum÷num
 davg ← {(sum÷num)⍵}
 digs ← '0123456789'
  dup ← {⍵ ⍵}
 dups ← dup each
 each ← {⍺←⊢ ⋄ ⍺ ⍺⍺¨⍵}
 fold ← ⌿
greet ← 'hello'twice
isnum ← ∊∘digs
  num ← ≢
  one ← 1
 plus ← +
 pred ← -∘one
 same ← succ∘pred
 succ ← one∘plus
  sum ← plus fold
 that ← {this ⍵}
 this ← {that ⍵}
twice ← {⍺⍺ ⍺⍺ ⍵}

    1 defs 'same'                   ⍝ "same" with names
same ← succ∘pred

⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ Top-down indented display:

    1 1 defs 'avg'                  ⍝ top-down defns of "avg"
avg ← sum÷num
  sum ← plus fold
    plus ← +
    fold ← ⌿
  num ← ≢

    1 1 defs ''                     ⍝ all defns top-down
avg ← sum÷num
  sum ← plus fold
    plus ← +
    fold ← ⌿
  num ← ≢
davg ← {(sum÷num)⍵}
dups ← dup each
  dup ← {⍵ ⍵}
  each ← {⍺←⊢ ⋄ ⍺ ⍺⍺¨⍵}
greet ← 'hello'twice
  twice ← {⍺⍺ ⍺⍺ ⍵}
isnum ← ∊∘digs
  digs ← '0123456789'
same ← succ∘pred
  succ ← one∘plus
    one ← 1
  pred ← -∘one
that ← {this ⍵}
  this ← {that ⍵}

    avl←{(⍳×/1↓⍴⍵)~⍵×⊃⍺⌷⍺⍺}         ⍝ some dfns
    box←{⍵⌿⍵/⍵ ⍵⍴⍳⍵×⍵}
    cmap←{⊂[⍳⍴⍴⍵]1∊¨⍵∘.=⍵}
    emt←{(,⍵=0)/,⍳⍴⍵}
    nxt←{(⍺(⍺⍺ avl)⍵)⊣@(⊂⍺)¨⊂⍵}
    nxtv←{⊃,/⍺∘(⍺⍺ nxt)¨⍵}
    rcb←{(⍳⍵),¨box⊃⍵*÷2}
    sfmt←{⊂[3 4]1 3 2 4⍉(2/(⍴⍵)*÷2)⍴⍵}
    sudoku←{sfmt⊃cmap∘rcb svec ⍵}
    svec←{⊃(⍺⍺⍴⍵)nxtv/(emt ⍵),⊂⊂⍵}

    1 1 defs 'sudoku'               ⍝ top-down display
sudoku ← {sfmt⊃cmap∘rcb svec ⍵}
  sfmt ← {⊂[3 4]1 3 2 4⍉(2/(⍴⍵)*÷2)⍴⍵}
  cmap ← {⊂[⍳⍴⍴⍵]1∊¨⍵∘.=⍵}
  rcb ← {(⍳⍵),¨box⊃⍵*÷2}
    box ← {⍵⌿⍵/⍵ ⍵⍴⍳⍵×⍵}
  svec ← {⊃(⍺⍺⍴⍵)nxtv/(emt ⍵),⊂⊂⍵}
    nxtv ← {⊃,/⍺∘(⍺⍺ nxt)¨⍵}
      nxt ← {(⍺(⍺⍺ avl)⍵)⊣@(⊂⍺)¨⊂⍵}
        avl ← {(⍳×/1↓⍴⍵)~⍵×⊃⍺⌷⍺⍺}
    emt ← {(,⍵=0)/,⍳⍴⍵}

⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ Bottom-up dependencies:

    1 ¯1 defs 'plus'                ⍝ defns dependent on "plus"
plus ← +
  succ ← one∘plus
    same ← succ∘pred
  sum ← plus fold
    avg ← sum÷num
    davg ← {(sum÷num)⍵}

    1 ¯1 defs 'one' 'plus'          ⍝ defns dependent on "one" and "plus"
one ← 1
  pred ← -∘one
    same ← succ∘pred
  succ ← one∘plus
plus ← +
  sum ← plus fold
    avg ← sum÷num
    davg ← {(sum÷num)⍵}

⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ changing definitions:

    defs 'plus ← -∘-'               ⍝ redefinition of "plus"

    0 defs 'same'                   ⍝ change to "plus" has changed "same"
same ← 1∘(-∘-)∘(-∘1)

    0 defs defs 'div←÷'             ⍝ new definition "div"
div ← ÷

See also: externs dft tacit in

Back to: contents

Back to: Workspaces