• No results found

So far, all uses of tuples have used a list based encoding, requiring discipline so as to avoid for example an attempt to project from an empty list. Such use come without a necessary guarantee of correctness and thus require an external proof to verify proper usage. This is due to a lack of usage of a value’s semantic information captured by its index. Although there is no direct encoding of “well-typed” tuples in L, by making use of terms’ index expression information, tuples can be recovered—still by using standard lists. In Haskell tuples not only provide a performance advantage but additionally a safe means of projecting the nth member which isn’t possible

using lists because they lack static size information at the type level. In L

by comparison since every list can have its shape arbitrarily restricted, a pair can be typed by a list with two members having indices i and j e.g.

somePair : Exp facts ictx TyList [i, j]

Then the first element can be projected by matching on the head of the list, and the second element can be projected by matching on the head of the tail. These matches are guaranteed to succeed. The first of which succeeds for the reasons formalized in safeHd. The second projection succeeds because the index expression of the tail can unify with the singleton list [j], the head of which clearly unifies with j as required. Using this reasoning, templates can be given projecting the first and second elements of list encoded pairs. The first projection is trivial

expFst : Exp facts ctx TyList [i, j] -> Exp facts ctx TyNat i

expFst {i=i}{j=j} e = Rewrite i

(\ixEnv , facts => reduceCaseList {ixEnv=ixEnv}) (CaseList e

"x" "xs'" (Reachable

(CanReach [RequireEq (var "x") (weaken i [_,_]) ,RequireEq (var "xs'") [weaken j [_,_]]] (\(x :: xs' :: ixEnv), knowns , [xEi, xs'Ej] =>

eqListPair (sym xEi) (sym xs'Ej))) (Rewrite (weaken i [_,_])

(\ixEnv , (ijEhdtl :: _) => fst (consInjective ijEhdtl)) (var "x")))

(Unreachable (NoReach (\ixEnv , facts , Refl impossible)) _ Z))

This largely follows the implementation of safeHd however because it is a template in concrete index expressions i and j, rather than a function

in free variables “i” and “j” the type of reachability is considerably more

interesting

[eval (thin i [("x", TyNat), ("xs'", TyList)]) (x :: xs' :: ixEnv)

,eval (thin j [("x", TyNat), ("xs'", TyList)]) (x :: xs' :: ixEnv)]

=

x :: xs'

Thus it must be the case that the evaluation of some arbitrary index expressions i and j are equal to the fresh index expressions xand xs0 which

trivially holds because these are precisely the two constraints that were requested.

Following safeHd a final reduction of the top-level CaseList index ex- pression must be performed by reduceCaseList. Intuitively the resulting index expression should hold trivially because the input e has index [i, j],

and the first branch returns x. However note the target index expression of

the rewrite on xwhich is a weakened copy ofi. Thus the resulting proof of

equality is not a simple assertion of reflexivity, but of weakening respecting index expression evaluation.

reduceCaseList : eval (CaseList [i, j]

,("xs'", TyList)]) Z) ixEnv = eval i ixEnv

reduceCastList {i=i}{ixEnv=ixEnv} = weakenRespectsEval i [_,_] ixEnv

Projecting the second element of the tuple is similar up to proofs on the reachability of case branches. Although the proofs are relatively simple they are most easily solved by goal refinement. Thus the program part of the expression is given leaving holes in positions of proofs.

expSnd : Exp facts ctx TyList [i, j] -> Exp facts ctx TyNat j

expSnd {i=i}{j=j} e = Rewrite j

(\ixEnv , facts => ?crushCasesToJ) (CaseList e

"x" "xs'" (Reachable

(CanReach [RequireEq (var "x") (weaken i [_,_]) ,RequireEq (var "xs'") [weaken j [_,_]]] (\(x :: xs' :: ixEnv), knowns , [xEi, xs'Ej] =>

eqListPair (sym xEi) (sym xs'Ej))) (CaseList (var "xs'")

"y" "xs''" (Reachable

(CanReach [RequireEq (var "y")

(weaken j [_,_,_,_]) ,RequireEq (var "xs''") []] (\(y :: xs'' :: x :: xs' :: ixEnv), (xxs'Eij :: knowns), [yEj, Refl] => ?xs'ConsReachable)) (var "y"))

(Unreachable (NoReach (\ixEnv , facts , eq => ?xs'NilUnreachable)) _ Z)))

(Unreachable (NoReach (\ixEnv , facts , Refl impossible)) _ Z))

To solve the above holes, first the reachability clauses are proven followed by the top-level refinement. The reachability in the first hole intuitively follows because the tail [j] of [i, j] is of course non-nil.

Solving ?xs'ConsReachable

The type of the hole?xs'ConsReachable asserts that evaluating the tail vari- able produces a non-empty list lookup ixEnv (There Here) = [y]. Recall that under each branch two fresh variables for the respective index expres- sions of the list head and tail are introduced. This allows for the environment to be unpacked into at least four values: unpacking the environment to y :: xs'' ... further refines the goal to xs' = [y]. This goal is solved relatively easily by matching on the reflected constraints. In fact by match- ing on the constraints var "xs''" = [] Idris has silently refined the right hand side of the equality in the goal to [y] rather than the slightly more cumbersome y :: xs''. In order to satisfy the obligation it should be asked what is known about xs' and y? Since y is fresh, only the reflected con- straint y = jis known. Additionally although no constraints were explicitly introduced on xs', from the initial successful match on e the constraint x :: xs' = [i, j] was pushed onto the context which is precisely what is required! Thus unpacking the head of the constraint context xxs'Eij brings this evidence into the proof context. The final proof is thus a simple exer- cise in rewriting. First the tail equality from xxs'Eij is projected yielding proof the proposition xs' = [j], thus the goal follows from the reflected constraint y = j. Concretely the goal is satisfied by

?xs'ConsReachable = rewrite yEj in

snd (consInjective xxs'Eij)

The next hole in expSnd captures fact that nil cannot be reached from the tail of [i, j]. Of course this follows from the fact that the cons of j :: [] is discriminated from [].

Solving ?xs'NilUnreachable

Proving that the nil case is unreachable from casing on the tail of "xs'" fol- lows from the fact that xs' = tail [i, j] which is clearly [j]. Concretely the type of the hole ?xs'NilUnreachable is cumbersome.

NotReachable

(RequireEq (var "y" :: var "xs''")

[weaken i [("x", TyNat), ("xs'", TyList)] ,weaken j [("x", TyNat), ("xs'", TyList)]] :: weakenConstraints facts [("x", TyNat)

,("xs'", TyList)]) (var "xs'")

[]

Which is more easily understood as

y ::xs0 =i ::jxs0 = [] =⇒ False

Of course by the injectivity of the list constructor this implies that xs0 = [j]

which by the additional assumption about xs0 gives the obviously contra-

dictory statement [] = [j]. This reasoning is directly given in Idris by an

explicit application of transitivity

?xs'NilUnreachable = consNotNil (sym xs'Enil `trans `

snd (consInjective yxs'Eij))) Where consNotNil simply discriminates between nil and cons using an

impossible pattern.

Having proven both reachability holes the only missing piece left is to perform the rewrite.

Solving ?crushCasesToJ

The requirement placed on ?crushCasesToJ involves proving that the two case statements yielding the head of the tail reduce in the obvious way, i.e.

eval (CaseList [i, j]

"x" "xs'" (CaseList (var "xs'") "y" "xs''" (var "y") Z)

Z) ixEnv = eval j ixEnv

Fortunately since no opaque definitions have been used Idris will prove this fact for us by normalization, the proof is a trivial application of reflexivity.

?crushCasesToJ = Refl

So far the use of the host system Idris has proven an effective means of performing theorem proving to ensure functional correctness of macro- defined functions. However thus far all definitions have been first-order. Incidentally by appealing again to the host Idris it’s possible to define a sort of higher-order function in L and prove their correctness which is the topic of the next section.