Skip to content

Direct Functions in Depth

The beginner tutorial introduced dfns. This tutorial covers the details: scoping rules, guard patterns, recursion techniques, and common idioms.

Scoping rules

Dfns use lexical scope. A variable assigned inside a dfn is local. An unassigned name looks outward through enclosing dfns to the workspace:

      x  100
      f  {y  10  x + y + }
      f 1
111

y is local to f. x is found in the workspace.

Nested dfns see their enclosing scope:

      outer  {
          scale  
          {scale × }
      }
      double  outer 2
      double 5
10

The inner dfn captures scale from outer. This is a closure.

Guard patterns

Multiple conditions

Guards are checked top to bottom. The first true guard returns:

      classify  {
           < 0   : 'negative'
           = 0   : 'zero'
          'positive'
      }

The last expression (no guard) is the default — reached only if all guards are false.

Guards with scalar arguments only

Guards expect a scalar boolean (0 or 1). If the condition produces an array, this is an error.

Recursion patterns

Simple recursion

      fact  {  1 : 1   ×   - 1}

Accumulator pattern

      fact  {  1    1 :   (×)  -1}

The default ⍺←1 lets it be called monadically. The left argument accumulates the result.

Recursion on arrays

      ⍝ Quicksort
      qsort  {1≥⍴ :   ( /⍨<p) , (/⍨=p) ,  /⍨>p[1]}

Common idioms

Identity / default

      {}               ⍝ identity (right tack equivalent)
      {  0   + }  ⍝ add with default left arg of 0

Pipeline style

      process  {
          data  clean 
          data  transform data
          summarise data
      }

Each step assigns to data, threading the computation through.

Anonymous dfns

Dfns don't have to be named:

      { × } 5         ⍝ square, inline
25
      { + }/ 10      ⍝ anonymous dfn as operand to reduce
55

Key points

  • Dfns use lexical scope — inner dfns can capture variables from outer dfns (closures)
  • Guards are checked top to bottom; the last unguarded expression is the default
  • enables recursion; use an accumulator pattern for tail-style recursion
  • Dfns are values: assign them, pass them to operators, use them inline

Next: Direct Operators