The Three Function Styles

So far, we have been reading and writing dfns.

         3 {⍺+⍵} 5              ⍝ Left argument ⍺, right argument ⍵
           {⍵>0:⍵,∇ ⍵-1 ⋄ ⍵}5   ⍝ Guard is : (colon). The function itself is ∇ (del)
      Fn ← {⍺⍵}                 ⍝ We can give functions names

It is also possible to name functions without explicitly referring to their arguments. This is called tacit or point-free programming.

      Plus ← +
      IndicesTo ← ⍳
      _Reduce ← /      
      Sum ← Plus _Reduce
      Sum IndicesTo 10

There is a syntax for composing functions called trains. We won’t look at them in detail now, but they allow some rather neat and memorable ways to write short functions.

      Mean ← +⌿ ÷ ≢         ⍝ The sum divided by the count
      Mean 3 1 4 1
      3 (+,-) 5             ⍝ Plus and minus
      ','(≠⊆⊢)'some,text'   ⍝ Split on commas

i Note: Small unnamed dfns and tacit functions expand your vocabulary. One of my favourites is the "split by delimiter" train (≠⊆⊢). It looks like a beat-up face kaomoji. A similar phrase which can take multiple delimiters can be found on aplcart.info.

Traditional functions

Dyalog is a modern APL implementation. Since the first APL implementations there has been a way of defining functions with a header line and named arguments and results. Since the introduction of dfns, functions of the original style are called traditional functions or tradfns.

      Mean ← +⌿÷≢         ⍝ A 3-train (fork) for the arithmetic mean
      Mean ← {(+⌿⍵)÷≢⍵}   ⍝ A dfn for the arithmetic mean
      ∇ m ← Mean a        ⍝ A tradfn for the arithmetic mean
        m ← (+⌿a) ÷ ≢a
      ∇

i Note: Copy and paste everything between (and including) the two del symbols into the session, and press Enter, to define a tradfn in your workspace.

Using Shift+Enter with the cursor on a name will bring up an editor window for that named thing.

A tradfn header reflects the calling syntax of the function.

     ∇ {result}←{optional}TradFn argument;local1;local2
[1]    :If 0=⎕NC'optional'                             
[2]        optional←'no left argument'                 
[3]    :EndIf                                          
[4]    local1←'⍺: ',optional                           
[5]    local2←'⍵: ',argument                           
[6]    global←⍪'TradFn last called with'local1 local2  
[7]    result←⍪local1 local2                           
     ∇      
  1. Try calling TradFn with one and two arguments. How can the result be made to display to the session?
  2. Inspect the variable global after calling TradFn with different arguments.
  3. Step through the function using Ctrl+Enter. Inpect ⎕NC'optional' when TradFn is called with one argument and when it is called with two arguments.

Here is the smallest tradfn:

      ∇ T
      ∇

T is a function which takes no arguments, returns no results and has no effects.

Results in {} curly braces do not print to the session by default, but can be passed to arguments. To ease debugging and write functions with predictable behaviour, it is generally best not to use shy results.

Optional left arguments are a little awkward in tradfns. The dfn equivalent is a little nicer looking: {⍺←'default' ⋄ ⍺,⍵}.

Which style to use?

While usage of different function styles varies throughout many applications, I suggest these non-prescriptive guidelines, from Adám’s APL Style Guide, for production code. When maintaining others’ code, it is best to try to continue in the already established style.

Dfns

For medium sized functions and utilities. Nested dfns are fine, but never use multi-line dfns inline.

 MultiDfn←{        ⍝ A Dfn with nested in-line multi-line dfns
     (3{           ⍝ These are confusing to read and trace through
         ⍺+2×⍵
     }⍺){
         x←⍺-4+⍵
         x-2×⍺
     }3+⍵
 }

Instead, give them names and then apply them. Named dfns should be multi-line so that they can be traced through, unless truly trivial.

 MultiDfn2←{    ⍝ The previous function rewritten more clearly
     y←3+2×⍺
     x←y-1+⍵
     x-2×y  
 }       

Do not use a dfn instead of naming a variable. For example, instead of

      r←{⍵/⍨10≤⍵}a,b

write

      candidates←a,b
      r←candidates/⍨10≤candidates

Tacit functions

Best as short, pure functions, performing some specific task such as data transformation. Trains and functions derived from functions and operators (e.g. +/) can be used inline if they are not too complex.

Tradfns

Best used as program control and for dealing with system interactions. The use of control structures can make procedural tasks easier to debug. For example, if an error occurs during a loop or iteration.

      ¯5{i←⍺+⍳⍵ ⋄ i÷i-2}10   ⍝ A single line function cannot be traced through

i Note: 💡 Use Ctrl+Enter to step through a multiline function. You can then use Shift+Enter to edit the function during execution and Esc to save your changes to the function and continue execution.

    ∇  r←a MultiLineError o;i
[1]    :For i :In a+⍳o       
[2]        r←i+3             
[3]        r÷r-2             
[4]    :EndFor               
    ∇    

Which style again?

  1. Which of the following function styles can have multiple lines?
    1. TradFns
    2. Dfns
    3. Tacit functions
  2. Which of the following function styles can be anonymous (unnamed)?
    1. Tradfns
    2. Dfns
    3. Tacit
  3. Think about which function style would be most appropriate in the following situations.
    1. Launching an application
    2. Applying a mathematical formula to an array of values
    3. A utility function to sort an array
    4. Reading and writing files
    5. Expressing the sum of two functions (f+g)(x)
    6. Downloading data from the internet
    7. GUI programming
    8. Checking if a function is a [no-op](https://en.wikipedia.org/wiki/NOP_(code) for a particular array
    9. Defining a piecewise mathematical function

Choo choo

  1. Translating functions
    1. Convert the following dfns into trains
      1. {⌈/≢¨⍵}
      2. {1+⍺-⍵}
      3. {∨/⍺∊⍵}
      4. {(⌽⍵)≡⍵}
    2. Convert the following trains into dfns
      1. (⌈/-⌊/)
      2. (+⌿÷1⌈≢)
      3. (⊢-|)
      4. (1∧⊢,÷)