The final new form to be discussed is the expression
function. As outlined in the Introduction, an expression function provides
the effect of a small function without the formality of introducing a
body. It is important to appreciate that strictly speaking an expression
function is basically another form of function and not another form of
expression. But it is convenient to discuss expression functions in this
chapter because like conditional expressions and quantified expressions
they arose for use with aspect clauses such as pre- and postconditions.

The syntax is

expression_function_declaration ::=

[overriding_indicator]

function_specification**is**

(expression)

[aspect_specification];

[overriding_indicator]

function_specification

(expression)

[aspect_specification];

As an example we can
reconsider the type Point and the function
Is_At_Origin thus

X, Y: Float := 0.0;

(P.X = 0.0

...

**end** P;

The expression function Is_At_Origin
is a primitive operation of Point just as
if it were a normal function with a body. If a type My_Point
is derived from Point then Is_At_Origin
would be inherited or could be overridden with a normal function or another
expression function. Thus an expression function can be prefixed by an
overriding indicator as indicated by the syntax.

Expression functions can have an aspect clause and
since by their very nature they will be short, this will frequently be
**with** Inline as in this example.

The result of an expression
function is given by an expression in parentheses. The parentheses are
included to immediately distinguish the structure from a normal body
which could start with an arbitrary local declaration. The expression
can be any expression having the required type. It could for example
be a quantified expression as in the following

(

This is another example of a situation where the
quantified expression does not need to be enclosed in its own parentheses
because the context supplied by the expression function provides parentheses.

Expression functions
can be completions as well as standing alone and this introduces a number
of possibilities. Remember that many declarations require completing.
For example an incomplete type such as

is typically completed
by a full type declaration later on

Completion also applies
to subprograms. Typically the declaration (that is the specification
plus semicolon) of a subprogram appears in a package specification thus

...

and then the body of
F which completes it appears in the body of
P thus

...

...

A function body cannot
appear in a package specification. The only combinations are

function declaration F | function body F |
---|---|

in spec of P | in body of P |

in body of P | in body of P |

none | in body of P |

Remember that mutual recursion may require that a
body be given later so it is possible for a distinct declaration of F
to appear in the body of P before the full
body of F. In addition to the above the function
body could be replaced by a stub and the proper body compiled separately
but that is another story.

The rules regarding
expression functions are rather different. An expression function can
be declared alone as in the example of Is_At_Origin
above; or it can be a completion of a function declaration and that completion
can be in either the package specification or body. A frequently useful
combination occurs with a private type where we need to make a function
visible so that it can be used in a precondition and the expression function
then occurs in the private part as a completion thus

X, Y: Float := 0.0;

(P.X = 0.0

...

**end** P;

Note that we cannot give an aspect specification
on an expression function used as a completion so it is given on the
function specification; this makes it visible to the user. (This rule
applies to all completions such as subprogram bodies and is not special
to expression functions.)

An expression function
can also be used in a package body as an abbreviation for

The possible combinations
regarding a function in a package are

function declaration F | expression function F |
---|---|

in spec of P | in spec or body of P |

in body of P | in body of P |

none | in spec or body of P |

We perhaps naturally
think of an expression function used as a completion to be in the private
part of a package. But we could declare a function in the visible part
of a package and then an expression function to complete it in the visible
part as well. This is illustrated by the following interesting example
of two mutually recursive functions.

(

(

These are the Male and Female functions described
by Hofstadter [14]. They are inextricably
intertwined and both are given with completions for symmetry.

Almost inevitably, at least one of the expression
functions in a mutually recursive pair will include an if expression
(or else **else**) otherwise the recursion will not stop.

Expression functions can also be declared in subprograms
and blocks (they are basic declarative items). Moreover, an expression
function that completes a function can also be declared in the subprogram
or block.

This is illustrated
by the following Gauss-Legendre algorithm which computes π to an
amazing accuracy determined by the value of the constant K.

(

(

(

T(N–1)–2.0**(N–1)*(A(N–1)–A(N))**2);

K: **constant** := 5; --* for example*

Pi:**constant** Long_Long_Float :=

((A(K) + B(K))**2 / (4.0*T(K));

**begin**

Put(Pi, Exp => 0);

New_Line;

**end** Compute_Pi;

Pi:

((A(K) + B(K))**2 / (4.0*T(K));

Put(Pi, Exp => 0);

New_Line;

With luck this will output 3.14159265358979324 (depending
on the accuracy of Long_Long_Float).

The functions A and B
give successive arithmetic and geometric means. They call each other
and so B is given as a function specification
which is then completed by the expression function.

I am grateful to Brad Moore and to Ed Schonberg for
these instructive examples.

The rules regarding
null procedures (introduced in Ada 2005 primarily for use with interfaces)
are modified in Ada 2012 to be uniform with those for expression functions
regarding completion. Thus

can be used alone as
a declaration of a null operation for a type or as a shorthand for a
traditional null procedure thus possibly completing the declaration

Expression functions and null procedures do not count
as subprogram declarations and so cannot be declared at library level.
Nor can they be used as proper bodies to complete stubs. Library subprograms
are mainly intended for use as main subprograms and to use an expression
function in that way would be somewhat undignified!

Thus if we wanted to
declare a useful function to compute sin 2*x* from time to time,
we cannot write

--

(2.0 * Sin(X) * Cos(X));

but either have to write it out the long way or wrap
the expression function in a package.

© 2011, 2012, 2013 John Barnes Informatics.

Sponsored in part by:

The Ada Resource Association: |
AdaCore: |
and Ada-Europe: |