Evaluation is an add-on
The word evaluation has so far been conspicuously absent. That is intentional.
IvoryScript has values and reduction. There is no general rule that an expression must repeatedly be transformed until some final value is obtained. An expression is already a value and reduction is explicitly controlled.
Evaluation nevertheless features, but has limited use.
Evaluation
IvoryScript provides the evaluation operator:
!e
Where #! denotes a single reduction,
! denotes reduction as far as required to obtain a
strict value.
For a value which is already strict, evaluation changes nothing. Thus:
!#k
denotes exactly the same value as:
#k
The distinction becomes significant where expression values are nested. Given:
e :: Exp (Exp Int)
a single reduction:
#!e
has type:
Exp Int
For example, #!(let { x = 6; y = 7 } in x * y)
denotes an Exp Int type, since the value of the
let reduction is the lazy x * y body
function application.
whereas:
!e
has type:
Int
Eval
Apart from the syntactic sugar of the !
operator, evaluation is entirely defined by the
Eval class and its three instances:
class Eval a, b where {
eval :: a -> b
}
For a strict type, evaluation is the identity:
instance Eval a, a | !a where {
inline eval = id
};
Where one reduction provides the required type:
instance Eval Exp b, b where {
inline eval x = #!x
};
The remaining case is recursive:
subordinate instance Eval Exp c, b where {
inline eval x = #!(eval (#!x))
};
Apart from the ! notation, these three instances
are the complete definition of evaluation in IvoryScript.
A strict value is unchanged. An expression may require one reduction. Where that is insufficient, evaluation is recursively applied to the value denoted by reduction.
Where evaluation remains useful
Most expressions occur in a context which constrains the
required type. Syntactic coercion can then introduce the
appropriate conversions. Where a particular reduction is
intended, #! denotes it directly.
Evaluation is principally useful where the surrounding context is unconstrained.
An isolated tuple is a typical example. Its components may contain expression values without a surrounding type requirement to determine how far they should be reduced. Evaluation can then explicitly request a strict value:
(!e1, !e2)
The same situation can arise for other otherwise unconstrained expressions, particularly at the outermost level of a script.