Function and Function Logic

Functions and Function Objects

We have to distiguish between functions (see chapter Functions) and function objects (this chapter).

Functions are declared at the toplevel of a module like the boolean functions ==>, and, ... Function objects are objects which represent functions and can be passed around as arguments to other functions. In other programming languages function objecs are sometimes called closures.

Functions can be converted to function objects and in many cases the compiler can do this conversion automatically.

We often use the term function for function objects as well, because it is usually clear from the context if a declared function or a function object is meant. However it has to be clear that functions and function objects are similar but different animals.

In this chapter we exclusively talk about function objects.

Function Type

The module function declares the type FUNCTION as the type of function objects.

-- file: function.ali
use
    predicate_logic
end

A: ANY
B: ANY

class
    FUNCTION[A,B]
inherit
    ghost ANY
end

The type FUNCTION[NATURAL,NATURAL] is the type of a function object which maps a natural number to a natural number.

By using tuples as the domain type it is possible to represent multiargument functions as well.

Since functions (aka function objects) are used very frequently in Albatross the type A->B can be used as a shorthand for FUNCTION[A,B]. The arrow operator is right associative so that A->B->C is parsed as A->(B->C) and represents the type FUNCTION[A,FUNCTION[B,C]] i.e. it represents a functions which maps an argument of type A to a function mapping an argument of type B to a value of type C.

Note that A->B->C and (A,B)->C are two different functions. The latter takes a tuple and maps it to a value of type C.

Equality of functions is not decidable. Therefore the type FUNCTION can inherit ANY only via a ghost inheritance relationship.

Function objects represent partial functions i.e. functions which might have preconditions. A total function is a special case of a partial function.

Basic Functions

The module function declares the following basic functions on function objects.

domain(f:A->B): ghost {A}
    -- The domain of the function 'f'

consistent(f,g:A->B): ghost BOOLEAN
        -- Do 'f' and 'g' have the same values on its common domain
    -> all(x) x in f.domain  ==>  x in g.domain  ==> f(x) = g(x)

(=) (f,g:A->B): ghost BOOLEAN
        -- Are the functions 'f' and 'g' equal?
    -> f.domain = g.domain and consistent(f,g)

All these functions are ghost functions because no algorithm is defined to compute their results.

Each function has a domain. The function (object) can be applied only to arguments in its domain. domain is a ghost function, i.e. it has to be proved that an argument is in the domain of a function in order to apply the function to the argument.

Two functions are consistent if they map arguments in their common domain to the same values.

Note that the expression all(x) f(x) = g(x) is an invalid expression because there is no guarantee that the functions f and g are applied to arguments in their domain. In the definition term of consistent the expression f(x) = g(x) is guarded by the two guards x in f.domain and x in g.domain and the lazy evaluation of ==> guarantees that f(x) = g(x) is evaluated only if the argument is in the corresponding domain.

Two functions are equal if the have the same domain are are consistent.

Function Expressions

Function objects are generated by function expressions. Type annotations are optional as long as the compiler can infer the type of the variable(s) and the return type.

Some examples of function expressions:

x -> x + 1

x:NATURAL -> x + 1

(x,y) -> x + y

(x,y:NATURAL): NATURAL -> x + y

agent(x)
    ensure
        -> x + 1
    end

agent(x:NATURAL):NATURAL
    ensure
        -> x + 1
    end

The general formats are

variable(s) -> expression

agent(variable(s)): optional_return_type
    require
        precondition_1
        precondition_2
    ensure
        -> expression
    end

The agent syntax allows to define function objects with preconditions.

Beta Reduction

Predicate and function expressions are very similar. They are expressed with a different syntax to allow more intuitive notations and to distighuish predicates which cannot have preconditions from functions which can have preconditions.

In both cases we have to declare a variable or a set of variables and an expression which calculates the result.

Function expressions like predicate expressions are equivalent to lambda expressions in lambda calculus. The Albatross compiler can do beta reductions with function expressions as well as shown in the following examples.

(x -> x + 1)(8*3)         ~>        8*3 + 1

((x,y) -> x * y)(a,7)     ~>        a * 7

Function Logic

The module function_logic uses function expressions to express more interesting things about functions.

E.g. we can define a function + so that f + (a,b) is the function f which has the same values as the function f except for a which maps to the value b. This function needs tuples (see chapter Tuple).

(+) (f:A->B, e:(A,B)): (A->B)
        -- The function 'f' except that it maps 'e.first' to e.second'.
    -> agent (a)
           require
               a = e.first or a in f.domain
           ensure
               -> if a = e.first then
                      e.second
                  else
                      f(a)
           end

Having this function it is possible to prove the theorems

all(f:A->B, a,x:A, b:B)
    ensure
         (f + (a,b))(a) = b

         x in f.domain ==> x /= a ==> (f + (a,b))(x) = f(x)
    end

The expression f|p should be the function f restricted to a domain whose arguments must be in f.domain and in p.

(|) (f:A->B, p:{A}): (A->B)
        -- The function 'f' whose domain is restricted to the arguments which are
        -- also in 'p'.
    -> agent (x)
           require
               x in f.domain
               x in p
           ensure
               -> f(x)
           end

The compiler can extract the domains of the resulting functions from the preconditions of the agent expressions.

(f + (a,b)).domain = {x: x = a or x in f.domain}

(f|p).domain       = {x: x in f.domain and x in p}

results matching ""

    No results matching ""