3.3 Index expression rewriting
3.3.1 Specifying index expression semantics
Given their relative simplicity, specifying the semantics of index expressions presented in figure 3.2 on page 70 within some variable environment is straightforward. Rather than performing substitution, a variable environ- ment is used to track the running values, drawn from the set V, bound
to each variable. A values is either a natural number or a list of natural numbers allowing for the obvious mapping from L types given in figure 3.6 following the style of the STLC example.
An environment is then either empty∅or an extension of the environment
ρ with a value v ∈ V, written v, ρ. In order to ensure the correctness of
variable lookup, environments must satisfy the well-formedness condition specified in figure 3.7. An environment is considered to be well-formed with respect to an underlying context ∆ if for every τ typed variable i ∈ ∆,
there is a mapping from i to some v of type JτK in ρ. In Idris the type of
environments follows precisely the formulation from the STLC, internalizing the correctness formulation.
Values Environments V ::= n∈N| V ::V | [] ρ::= V, ρ| ∅ ∆`ρ (ρ is a well-formed environment) WfEmpty ∅ ` ∅ WfCons v ∈JτK ∆`ρ (i, τ),∆`v, ρ
Figure 3.7: Program evaluation environments
data IxEnv : IxCtx -> Type where
Nil : IxEnv []
(::) : EvalTy k -> IxEnv xs -> IxEnv ((x, k) :: xs)
As described earlier in section 2.5, an important notion of correctness to be shown for a traditional operational semantics is type-safety which was initially explored by Wright and Felleisen [WF94]. Type-safety is constituted by two theorems known as progress and preservation respectively guaranteeing that non-value terms can always reduce and that as the term is reduced, its type is invariant. As in the case of STLC leveraging dependent types and embedding the language as an EDSL allows for a simpler notion of type-safety induced by the host type system to be proven.
First by defining evaluation as a total function the system guarantees that every input index expression will result in a meaningful semantic value. Moreover since type-checking will have taken place prior to any other work, the input program will always be known to be well-typed and well-scoped. Second, by taking the evaluation function to be dependent on the type of the input, we can guarantee that the type of the input index expression is preserved in the output value. Thus type-safety becomes an intrinsic part of evaluation. Again in the style of Augustsson a tagless evaluator can be given
Listing 3.2: Semantics of index expressions eval : Ix ictx t -> IxEnv ictx -> EvalTy t
eval (S i) ixEnv = S (eval i ixEnv) eval Z ixEnv = Z
eval (Var elt) ixEnv = lookup ixEnv elt eval [] ixEnv = []
eval (i :: is) ixEnv = eval i ixEnv :: eval is ixEnv eval {k=k} (CaseList {k=k} i1 x xs i2 i3) ixEnv =
listDestruct (const (EvalTy k))
(eval i3 ixEnv) (eval i1 ixEnv)
eval (ElimList i1 acc x i2 i3) ixEnv =
listFold (\n, vacc => eval i2 (vacc :: n :: ixEnv)) (eval i3 ixEnv)
(eval i1 ixEnv)
eval (ElimNat i1 acc x i2 i3) ixEnv =
natFold (\n, vacc => eval i2 (vacc :: n :: ixEnv)) (eval i3 ixEnv)
(eval i1 ixEnv)
In an abuse of notation, as a notational convenience, eval i p may be written using the standard notation JiKρ. Evaluation proceeds in a fashion
similar to the STLC evaluator with case and iterator evaluation appeal- ing to the auxiliary functions defined in appendix C.1 on page 193. The listDestruct function performs dependent case analysis, however the use of a function simplifies the proof of some necessary theorems about evaluation. Empirically Idris also performs better and termination checking is aided.
Later we will show that this notion of type-safety can both be extended to semantics indexed expressions, and that it’s possible to internalize this property in a way that admits the correct by construction approach to expression evaluation.
Having defined a denotation for index expressions it’s possible to give a notion of semantic equivalence of index expressions with respect to an environment. Two definition are presented in figure 3.8. The first i ≈ρ j asserts that the index expressions i and j can only equated given some
environment ρ mapping their variables to values. The second asserts that i is equivalent to j if they are semantically equal in every well-formed
environment. Intuitively these definitions can be taken to capture the notion that the index expressions i andj are equivalent when they agree under all
valid parallel substitutions of free variables tracked by the context ∆.
As suggested by the name, index expression equivalence naturally forms an equivalence relation on well-formed index expressions and environments. This follows from the fact that at its heart, index expression equivalence is an appeal to judgmental equality.
Theorem 3.3.1. Index expression conversion i ≈j forms an equivalence
relation.
The definitions of index expression equivalence within an EDSL is trivial thanks to the requirement that all index expressions are well-formed by def-
S ≈ρS (index expression equivalence) IxEq ∆`ρ ∆`i:τ ∆`j :τ JiKρ=vi JjKρ=vj vi =vj ∆`i≈ρj
S ≈S (universal index expression equivalence)
IxConv
∆`i:τ ∆`j :τ
∀ρ.i≈ρj
∆`i≈j
Figure 3.8: Semantic equivalence of index expressions
inition. In Idris it’s just an equality on evaluation, intuitively capturing the idea that index expression equivalence allows for rewriting in an evaluation respecting way.
{i, j : Ix ictx t} -> (ixEnv : IxEnv ictx) -> eval i ixEnv = eval j ixEnv
Recall that the construction of index expression equivalence was a neces- sary requirement towards the ultimate goal of giving a sound, general means of rewriting term index expressions.