dfns.dyalog.com

Dyalog APL

  • ️The D-Functionistas
Translation of D-functions into tacit form
------------------------------------------
The following monadic functions are said  to  be  "extensionally equal"  because
given the same argument they  return  the  same  result.  Conversely,  they  are
"intensionally distinct" as their codings differ.
    {⍬⍴⍴⍵}      ⍝ D-function
    (⍬⍴⍴)       ⍝ Fork
    ⍬∘⍴∘⍴       ⍝ Composition
NB: these functions are extensionally equal only in a monadic context: if a left
argument  is  supplied,  their  behaviours diverge: the dfn ignores it; the fork
uses it; and the composition objects to it.
(muse:
    A significant difference between the dfn coding and the other  two  is  that
    the dfn's curly braces serve as a "quoting  mechanism" with respect  to  the
    dereferencing  (look-up)  of  names.  Names  in  dfns  are  dereferenced  at
    _application_ time, while those in trains and compositions are  dereferenced
    at  _definition_ time:
          tax ← 10                              ⍝ 10% tax
          dfn ← {⍵×1+tax÷100}                   ⍝ dfn including-tax calculation
        train ← (1+tax÷100)×⊢                   ⍝ train     ..      ..      ..
         comp ← (1+tax÷100)∘×                   ⍝ composition       ..      ..
        (dfn, train, comp) 20                   ⍝ tests: 20 + tax → 22
    22 22 22
        dfn ⋄ train ⋄ comp                      ⍝ display of functions
    {⍵×1+tax÷100}
    1.1×⊢
    1.1∘×
        tax ← 12                                ⍝ tax hike to 12%
        (dfn, train, comp) 20                   ⍝ only dfn uses new tax rate
    22.4 22 22
)
The following seven rules, applied recursively, transform a single line dfn into
tacit form:
    {X f Y} → ({X} f {Y})       ⍝ fgh-fork                        rule: {XfY}
    {  f Y} → (    f {Y})       ⍝  gh-atop                              {fY}
    {A f Y} → ( A  f {Y})       ⍝ Agh-fork                              {AfY}
    {X f A} → (A(⊢f⊣){X})       ⍝ commute                               {XfA}
        {⍺} → ⊣                 ⍝ left (base case)                      {⍺}
        {⍵) → ⊢                 ⍝ right (base case)                     {⍵}
        {A} → ((A)⊣⊣)           ⍝ constant (unusual base case)          {A}
where:
    X, Y are array-valued expressions with either ⍺ or ⍵ free.
       A is a constant array-valued expression, with neither ⍺ nor ⍵ free.
       f is a function-valued expression, with neither ⍺ nor ⍵ free.
    where:
        ⍺ (resp ⍵) is said to occur "free" in an expression if any occurrence of
        it is not enclosed in curly braces.  For example, ⍺ (but not ⍵) is  free
        in expression (⍺+2{⍺+⍵}3).
Translation example:
        {(2+⍺)×⍵÷3}             ⍝ rule {XfY}, × is principal (leftmost outer) fn
        ¯¯¯¯¯¯¯¯¯¯¯
    →   ({2+⍺}×{⍵÷3})           ⍝  ..  {AfY}
         ¯¯¯¯¯
    →   ((2+{⍺})×{⍵÷3})         ⍝  ..  {⍺}
            ¯¯¯
    →   ((2+⊣)×{⍵÷3})           ⍝  ..  {XfA}
               ¯¯¯¯¯
    →   ((2+⊣)×{3(⊢÷⊣)⍵})       ⍝  ..  {AfY}
               ¯¯¯¯¯¯¯¯¯
    →   ((2+⊣)×(3(⊢÷⊣){⍵}))     ⍝  ..  {⍵}
                      ¯¯¯
    →   ((2+⊣)×(3(⊢÷⊣)⊢))       ⍝ tacit form
For vector (strand) syntax we need a further rule:
    X Y Z ...  → ((⊂X),(⊂Y),(⊂Z),...)                                   (X Y)
where X, Y, Z, ... are array-valued expressions.
Square-bracket indexing should be replaced with the equivalent index function:
    X[Y] → ((⊂Y)⌷X)                                                     X[Y]
and embedded assignments replaced with inner dfns:
    {n+n←1+⍵} → {{⍵+⍵}1+⍵}                                              (←)
These rules are sufficient to translate single-line dfns into tacit  form.  How-
ever, the resulting function train will typically be longer than  necessary. For
example:
         {(+⌿⍵)÷≢⍵}             ⍝ {XfY}        "mean" (ignores left argument)
         ¯¯¯¯¯¯¯¯¯¯
    →   ({+⌿⍵}÷{≢⍵})            ⍝ {fY} {fY}
         ¯¯¯¯¯ ¯¯¯¯
    →   (((+⌿){⍵})÷((≢){⍵}))    ⍝ {⍵} {⍵}
              ¯¯¯      ¯¯¯
    →   (((+⌿)⊢)÷((≢)⊢))        ⍝ removing superflous parentheses       (())
          ¯  ¯    ¯ ¯
    →   ((+⌿⊢)÷(≢⊢))            ⍝ final non-optimal form (see below)
Optimisation
¯¯¯¯¯¯¯¯¯¯¯¯
     (f⊢)g(h⊢) → (f g h)⊢       ⍝ ⊢ factoring                           (⊢g⊢)
     (f⊢)g ⊢   → (f g ⊢)⊢       ⍝ ⊢ factoring                           (⊢g⊢)
       ⊣ g ⊢   → g              ⍝ dyadic function application           (⊣g⊢)
      (f(g h)) → ((f g)h)       ⍝ association                           (f(gh))
leading to a shortening of the above example "mean":
    →   ((+⌿⊢)÷(≢⊢))            ⍝ (⊢g⊢) (())
         ¯¯¯¯¯¯¯¯¯¯
    →   (+⌿÷≢)⊢                 ⍝ optimal form
where the final ⊢ is present only to ignore an unwanted left argument.
More examples:
        {(⊂⍋⍵)⌷⍵}               ⍝ {XfY}         "sort" (Phil Last)
        ¯¯¯¯¯¯¯¯¯
    →   ({⊂⍋⍵}⌷{⍵})             ⍝ {fY} {⍵}
         ¯¯¯¯¯ ¯¯¯
    →   ((⊂{⍋⍵})⌷⊢)             ⍝ {fY} {⍵}
           ¯¯¯¯
    →   ((⊂(⍋⊢))⌷⊢)             ⍝ (f(gh))
         ¯¯¯¯¯¯¯
    →   (((⊂⍋)⊢)⌷⊢)             ⍝ (⊢g⊢)
              ¯  ¯
    →   ((⊂⍋)⌷⊢)⊢               ⍝ (⊢g⊢)
        {(⊂⍬),⍵}                ⍝ {AfY} {⍵}
        ¯¯¯¯¯¯¯¯
    →   ((⊂⍬),⊢)                ⍝ (see optimisation below)
        {⍵ ⍵}                   ⍝ (X Y)
         ¯¯¯
    →   {(⊂⍵),(⊂⍵)}             ⍝ {XfY}
        ¯¯¯¯¯¯¯¯¯¯¯
    →   ({⊂⍵},{⊂⍵})             ⍝ {fY} {⍵}
         ¯¯¯¯ ¯¯¯¯
    →   ((⊂⊢),(⊂⊢))             ⍝ (⊢g⊢)
         ¯¯¯¯¯¯¯¯¯
    →   (⊂,⊂)⊢
        {⍵[⍺]}                  ⍝ X[Y]
         ¯¯¯¯
    →   {(⊂⍺)⌷⍵}                ⍝ {XfY} {⍵}
        ¯¯¯¯¯¯¯¯
    →   ({⊂⍺}⌷⊢)                ⍝ {fY} {⍺}
         ¯¯¯¯¯¯
    →   ((⊂⊣)⌷⊢)
        {32+⍵×1.8}              ⍝ {AfY}         °C → °F
        ¯¯¯¯¯¯¯¯¯¯
    →   (32+{⍵×1.8})            ⍝ {XfA} {⍵}
            ¯¯¯¯¯¯¯
    →   (32+(1.8(⊢×⊣)⊢))        ⍝ (⊢×⊣) → ×  (× is commutative)
                ¯¯¯¯¯
    →   (32+(1.8×⊢))
The last example results in a form for which, unlike the original dfn coding, ⍣
has an inverse:
        F ← 32+(1.8×⊢)          ⍝ °C → °F
        F ¯273.15 ¯40 0 100     ⍝ Fahrenheit from Celsius
    ¯459.67 ¯40 32 212
        C ← F⍣¯1                ⍝ °F → °C
        C ¯459.67 ¯40 32 212    ⍝ Celsius from Fahrenheit
    ¯273.15 ¯40 0 100
Restrictions and Extensions
---------------------------
[1] The process cannot translate an expression that contains ⍺ or ⍵ free in the
    operand of an operator:
        {(⊂⍣⍵)'Doh!'}
            ¯
[2] Nor will it transform a recursive call ∇.  Although it seems likely (to JMS)
    that  rules  might  be discovered for replacing some cases of recursion with
    the power operator ⍣.
[3] For multi-line dfns,  Phil Last has shown us how to transform a guard into a
    simple expression using /. See examples in →pow← and →cond←.
        {C:T⋄F}  →  {⊃{F}/(⍳~C),{T}/(⍳C),⊂⍵}                ⍝ (:⋄)
    where C, T and F are expressions. Unfortunately, guard expression C is eval-
    uated twice in the transformed code.  Note, however, that if neither T nor F
    makes  reference  to ⍺, then expression C may be evaluated just once and its
    result passed as left argument to an inner function:
        {C:T⋄F}  →  ((C){⊃{F}/(⍳~⍺),{T}/(⍳⍺),⊂⍵}⊢)          ⍝ (:⋄) T,F "monadic"
    (muse:
        There was overwhelming cultural pressure, in the specification of primi-
        tive power operator ⍣, for the "repeat count" to be the right _operand_.
        Had  the  repeat count been assigned to the left _argument_ of a monadic
        operator, as in →pow←, rule (⋄) could have been the slightly simpler:
            {C:T⋄F}  →  {(~C){F}⍣ C{T}⍣ ⍵}                  ⍝ (:⋄)
                                ¯     ¯
        Oh, and the _monadic_ derived function could have been "fixpoint":
            (1+÷)⍣ 1        ⍝ 1+÷ 1+÷ ... 1
        1.61803  ¯
    )
There is plenty of scope for further optimisation, particularly by employing the
compose (∘) and commute (⍨) operators:
   (A g ⊢) → A∘(g) ⊢
    (f g)⊢ → f∘(g) ⊢
     (⊢g⊣) → g⍨
       f⍨⍨ → f
      f⍨∘A → A∘(f)
    A∘(f⍨) → (f∘A)
          ...
On optimisation:  "By what course of calculation can these results be arrived at
by the machine in the shortest time?" - Passages from the Life of a Philosopher,
Charles Babbage, 1864, p.137.
Tacit programming in the large
------------------------------
It is possible to write substantial programs using predominatly tacit style. See
Aaron Hsu's Co-dfns compiler for a high-performance parallel version of APL:
    https://github.com/arcfide/Co-dfns
Source files c.cd, d.cd, e.cd and f.cd are good examples.
Examples:
        F ← 32+(1.8×⊢)
    →   F ← 32+1.8∘×
        ((⊂⍬),⊢)
    →   (⊂⍬)∘,
See also: dft defs
Back to: contents
Back to: Workspaces