4.2. def: Define Function

The def declaration defines a function. A function definition consists of an identifier, a sequence of parameters, and a body expression.

An Orc function behaves much like a function, method, procedure, or subroutine in other programming languages. However, there are two key differences:

Orc functions have additional features, many of them adopted from typed functional programming languages:

4.2.1. Syntax

[22]DeclareDefinition::= def Variable TypeParameters? Parameters ReturnType? Guard? = Expression  
[64]Parameters::= ( Pattern , , Pattern )  
[24]Guard::= if ( Expression )  

4.2.2. Function Execution

The simplest case of argument binding uses only variables and wildcards as parameters.

When a function is called, the function body executes, and in parallel the argument expressions of the call are deflated.

If the execution of the body encounters a use of a parameter whose corresponding argument expression has not yet published a value, that use blocks until the argument value is available, but the rest of the body continues to execute.

Whenever the execution of the body would publish a value, the function call publishes that value. If execution of the body halts, the function call also halts. As a result, a function call might publish any number of times, including zero.

If the function call is killed, execution of the body expression is also immediately killed.

Because functions are lenient, the following two programs are equivalent:

def fn(x,y) = E
fn(G,H)

val x = G
val y = H
E

4.2.3. Patterns as Parameters

A function parameter may be any pattern. A lenient pattern is either a variable pattern or a wildcard pattern; such patterns will never fail to match. Any other pattern is a strict pattern, which could fail to match.

When a function is called, the call blocks until a value is available for each strict pattern. The values are then matched against the strict patterns. If all of these matches succeed, then the function call executes as described earlier. If any strict pattern match fails, or if any of the argument expressions corresponding to a strict pattern halts, then the function call halts.

Suppose P is a strict pattern. The following two programs are equivalent:

def fn(x,P) = E
fn(G,H)

val x = G
val z = H
z >P> E

4.2.4. Recursion

Functions can be recursive; that is, the name of a function may be used in its own body.

{- A recursive factorial function -}
def fact(n) = if (n <= 1) then 1 else n * fact(n-1)

A recursive function might continue executing indefinitely, producing an infinite number of publications.

{- Publishes a signal every second, forever -}
def metronome() = signal | Rwait(1000) >> metronome()

A set of functions may be mutually recursive by naming each other in their bodies. There is no special keyword for mutual recursion; whenever two or more function definitions are adjacent, they are allowed to mutually recurse.

4.2.5. Guards

A function definition may include a guard, of the form if ( E ). When a function is called, and each strict pattern matches successfuly as described earlier, then the guard expression E is deflated. If E deflates to true, then the function body is executed. If E deflates to some other value, or halts without publishing a value, then the function call halts silently.

Suppose P is a strict pattern and Gd is a guard expression. The following two programs are equivalent:

def fn(x,P) if (Gd) = E
fn(G,H)

val x = G
val z = H
z >P> Ift(Gd) >> E

4.2.6. Clausal Definition

A function can be defined by a sequence of clauses: repeated function definitions with the same identifier but different parameters. Each clause must have the same number of parameters.

When a function with multiple clauses is called, the argument expressions are deflated, and in parallel the first clause is executed. The clause will fail under any of the following conditions:

  1. One of the parameters is a strict pattern, and that pattern fails to match.

  2. One of the parameters is a strict pattern, and the corresponding argument expression has halted silently.

  3. There is a guard expression, and it did not deflate to true.

If each strict pattern matches successfully, and the guard expression (if present) deflates to true, then the corresponding function body executes. If the clause fails, then the next clause is executed. If the last clause fails, then the function call halts silently.

4.2.7. Type

When a function is defined, the function identifier is bound to a closure. A definition must be given additional type information so that the typechecker can deduce the correct function type for the identifier.

4.2.8. Examples

Available Pairs
{- 
  Publish pairs of results from three computations, as they become available.
-}
def pairs(x, y, z) = (x, y) | (x, z) | (y, z)
			
pairs(Rwait(2000) >> 0, 1, Rwait(1000) >> 2)

{-
OUTPUT:
(1, 2)
(0, 1)
(0, 2)
-}
{-
OUTPUT:
(1, 2)
(0, 2)
(0, 1)
-}
Parallel-Or Function
{- 
  Define a parallel-or function. 
-}
def parallelor(x,y) = 
  val first = Ift(x) >> true | Ift(y) >> true | (x || y)
  first
			
parallelor(false, Rwait(1000) >> true)

{-
OUTPUT:
true
-}
Even/Odd Using Mutual Recursion
{- Test if a number is even or odd, using mutual recursion -}
def even(n) = Ift(n = 0) >> true
            | Ift(n <: 0) >> odd(n+1) 
            | Ift(n :> 0) >> odd(n-1)
    
def odd(n) = Ift(n = 0) >> false
           | Ift(n <: 0) >> even(n+1) 
           | Ift(n :> 0) >> even(n-1)

odd(-4)

{- 
OUTPUT:
false
-}
List Head
{- 
  Publish the head of a nonempty list.
  If the list is empty, halt silently.
-}
def head(h:_) = h

head([2, 3]) | head([])

{-
OUTPUT:
2
-}
List Length
{- Find the length of a list -}
def length([]) = 0
def length(_:rest) = length(rest) + 1
			
length([1, 2, 4])

{-
OUTPUT:
3
-}
List Sum
{- Sum the elements of a list -}

def sum([]) = 0
def sum(h:t) = h + sum(t)
sum([1, 2, 3])

{-
OUTPUT:
6
-}

Same-Length Zip
{- 
  "Zip" a pair of lists together into a list of pairs.
  If the lists are of inequal length, halt silently.
-}
def zip(x:xs, y:ys) = (x, y):zip(xs, ys)
def zip([], []) = []

  zip([0, 1], [false, true]) 
| zip([1, 2, 3], signal)

{-
OUTPUT:
[(0, false), (1, true)]
-}

Fibonacci Function
{- Fibonacci numbers -}

def fib(0) = 1
def fib(1) = 1
def fib(n) if (n :> 1) = fib(n-1) + fib(n-2)

fib(5)

{-
OUTPUT:
8
-}

4.2.9. Related Links

Related Tutorial Sections