问题
In this answer on a question about the totality checker, a workaround involving using case
instead of with
was recommended.
However, in situations where the result of the match refines the type of other arguments, this transformation is not straightforward to make.
For example, given the following definitions:
data IsEven : Nat -> Nat -> Type where
Times2 : (n : Nat) -> IsEven (n + n) n
data IsOdd : Nat -> Nat -> Type where
Times2Plus1 : (n : Nat) -> IsOdd (S (n + n)) n
total parity : (n : Nat) -> Either (Exists (IsEven n)) (Exists (IsOdd n))
the following definition typechecks, but is not accepted as total:
foo1 : Nat -> Nat
foo1 n with (parity n)
foo1 n@(k + k) | Left (Evidence _ (Times2 k)) = (n * n)
foo1 n@(S (k + k)) | Right (Evidence _ (Times2Plus1 k)) = (2 * (k * n + k))
whereas the following one doesn't typecheck:
foo2 : Nat -> Nat
foo2 n = case parity n of
Left (Evidence k (Times2 k)) => n * n
Right (Evidence k (Times2Plus1 k)) => (2 * (k * n + k))
due to
Type mismatch between
IsEven (k + k) k (Type of Times2 k)
and
IsEven n k (Expected type)
I also tried expanding the with in foo1:
foo1' : (n : Nat) -> Either (Exists (IsEven n)) (Exists (IsOdd n)) -> Nat
foo1' n@(k + k) (Left (Evidence k (Times2 k))) = (n * n)
foo1' n@(S (k + k)) (Right (Evidence k (Times2Plus1 k))) = 2 * (k * n + k)
foo1 : Nat -> Nat
foo1 n = foo1' n (parity n)
but here, foo1'
is also not accepted as total:
foo1' is not total as there are missing cases
回答1:
One effective workaround I've found for this is to re-bind n
in each branch, i.e. to write foo2
as
foo2 : Nat -> Nat
foo2 n = case parity n of
Left (Evidence k (Times2 k)) => let n = k + k in n * n
Right (Evidence k (Times2Plus1 k)) => let n = S (k + k) in (2 * (k * n + k))
Unfortunately, while this works for this simplified problem from my original question, it doesn't work when the types of the various branches differ; for example in
total Foo : Nat -> Type
Foo n = case parity n of
Left (Evidence k (Times2 k)) => ()
Right (Evidence k (Times2Plus1 k)) => Bool
foo : (n : Nat) -> Foo n
foo n = case parity n of
Left (Evidence k (Times2 k)) => ()
Right (Evidence k (Times2Plus1 k)) => True
which fails with
Type mismatch between
() (Type of ())
and
case parity (plus k k) of
Left (Evidence k (Times2 k)) => ()
Right (Evidence k (Times2Plus1 k)) => Bool (Expected type)
来源:https://stackoverflow.com/questions/46563713/case-that-refines-arguments