My Code
My piece of code, i.e.:
equal_suffix: Eq a => List a -> List a -> List a
equal_suffix u v with (snocList u)
equal_suffix _ _ | Empty = []
equal_suffix _ v | (Snoc x xs snx) with (snocList v)
equal_suffix _ _ | (Snoc x xs snx) | Empty = []
equal_suffix _ _ | (Snoc x xs snx) | (Snoc y ys sny) = case x == y of
False => []
True => (equal_suffix xs ys | snx | sny) ++ [x]
does not type check, with compile yelling:
Error: With clause does not match parent
Hello:857:25--857:38
853 |
854 | equal_suffix: Eq a => List a -> List a -> List a
855 | equal_suffix u v with (snocList u)
856 | equal_suffix _ _ | Empty = []
857 | equal_suffix _ v | (Snoc x xs snx) with (snocList v)
^^^^^^^^^^^^^
My Question(s)
My first question is:
What does parent mean for a with clause in the context of Idris (Idris2 to be specific)? (Edit1: Thanks to #joel I now understand)
And if the first question can't help me understand this:
What is the problem with my code? (Edit1: Original Final Question)
What is the correct syntax of using nested with clause in Idris(2)? (Edit1: A More Focused Question thanks to the reminder from the comment #Vega)
(Excuse my snake-cased Pythonic mind for the naming of the variables :D)
Edit1 (2022-05-09):
The question was closed because the original last question is not focused enough. I adjust my question hopefully to reopen this as I am still in trouble with the with clause still.
What I have tried
To add more details, I did try the #joel version myself independently, i.e., to only case-split after specifying all with statement:
func: input_type -> output_type
func input with (view1 input)
func input | input_in_view1 with (view2 input)
func input | input_in_view1 | input_in_view2 = ...
I also tried to make a tuple of the two views, i.e.,
func: input_type -> output_type
func input with (view1 input, view2 input)
func input | (input_in_view1, input_in_view2) = ...
However, these two do not seem to work (the first leads to an unsolved hole error and the second non-covering error or non terminating type checking depending on how I case-split) .
Example from TDD by Edwin
This equal_suffix exercise is from TDD by Edwin. I was still stuck and stopped at exactly this exercise.
Here is an example in the book which I copy as follows:
isSuffix : Eq a => List a -> List a -> Bool
isSuffix input1 input2 with (snocList input1)
isSuffix [] input2 | Empty = True
isSuffix (xs ++ [x]) input2 | (Snoc rec) with (snocList input2)
isSuffix (xs ++ [x]) [] | (Snoc rec) | Empty = False
isSuffix (xs ++ [x]) (ys ++ [y]) | (Snoc rec) | (Snoc z)
= if x == y then isSuffix xs ys | xsrec | ysrec
else False
where snocList is redefined in the book for educational purpose and rec might be just a typo of xsrec and ysrec respectively.
Recursing on with clause directly for the totality.
I did manage to make it work when changing the recursive call
equal_suffix xs ys | snx | sny
to
equal_suffix xs ys
However, according to the book P275, to make a totally defined function, we need to recurse on the with clause directly to tell compiler about the relation between the recursive calls, so I want the function to be defined by directly calling the with clause recursively.
Simplest fix: remove | sny in your final line. Apparently it's not needed.
"Parent" presumably means parent function call in the recursive loop.
Now as to why it doesn't compile with | sny ... my guess is your second view is only present in one branch of your pattern match, whereas it needs to be present in both if you're going to pass sny recursively in equal_suffix xs ys | snx | sny.
You might be able to get something like this to work
equal_suffix: Eq a => List a -> List a -> List a
equal_suffix u v with (snocList u)
equal_suffix u v | su with (snocList v)
equal_suffix [] _ | Empty | _ = []
equal_suffix _ [] | _ | Empty = []
equal_suffix (xs ++ [x]) (ys ++ [y]) | (Snoc x xs snx) | (Snoc y ys sny) =
case x == y of
False => []
True => (equal_suffix xs ys | snx | sny) ++ [x]
but this particular example fails to compile with unsolved holes.
Related
I'd like to use views and with in my function signature without defining an extra type-level function. Is this possible? Say I have
import Data.List.Views
data Foo : List Nat -> Type where
What I've tried
In a case
bar : Foo xs -> case xs with (snocList xs)
[] | Empty => ?rhs
ys + [y] | Snoc y ys rec => ?rhs'
but that's a syntax error. Not sure how I'd do this in a let block.
Let's say we have a function merge that, well, just merges two lists:
Order : Type -> Type
Order a = a -> a -> Bool
merge : (f : Order a) -> (xs : List a) -> (ys : List a) -> List a
merge f xs [] = xs
merge f [] ys = ys
merge f (x :: xs) (y :: ys) = case x `f` y of
True => x :: merge f xs (y :: ys)
False => y :: merge f (x :: xs) ys
and we'd like to prove something clever about it, for instance, that merging two non-empty lists produces a non-empty list:
mergePreservesNonEmpty : (f : Order a) ->
(xs : List a) -> (ys : List a) ->
{auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
NonEmpty (merge f xs ys)
mergePreservesNonEmpty f (x :: xs) (y :: ys) = ?wut
Inspecting the type of the hole wut gives us
wut : NonEmpty (case f x y of True => x :: merge f xs (y :: ys) False => y :: merge f (x :: xs) ys)
Makes sense so far! So let's proceed and case-split as this type suggests:
mergePreservesNonEmpty f (x :: xs) (y :: ys) = case x `f` y of
True => ?wut_1
False => ?wut_2
It seems reasonable to hope that the types of wut_1 and wut_2 would match the corresponding branches of merge's case expression (so wut_1 would be something like NonEmpty (x :: merge f xs (y :: ys)), which can be instantly satisfied), but our hopes fail: the types are the same as for the original wut.
Indeed, the only way seems to be to use a with-clause:
mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
mergePreservesNonEmpty f (x :: xs) (y :: ys) | True = ?wut_1
mergePreservesNonEmpty f (x :: xs) (y :: ys) | False = ?wut_2
In this case the types would be as expected, but this leads to repeating the function arguments for every with branch (and things get worse once with gets nested), plus with doesn't seem to play nice with implicit arguments (but that's probably worth a question on its own).
So, why doesn't case help here, are there any reasons besides purely implementation-wise behind not matching its behaviour with that of with, and are there any other ways to write this proof?
The stuff to the left of the | is only necessary if the new information somehow propagates backwards to the arguments.
mergePreservesNonEmpty : (f : Order a) ->
(xs : List a) -> (ys : List a) ->
{auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
NonEmpty (merge f xs ys)
mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
| True = IsNonEmpty
| False = IsNonEmpty
-- for contrast
sym' : (() -> x = y) -> y = x
sym' {x} {y} prf with (prf ())
-- matching against Refl needs x and y to be the same
-- now we need to write out the full form
sym' {x} {y=x} prf | Refl = Refl
As for why this is the case, I do believe it's just the implementation, but someone who knows better may dispute that.
There's an issue about proving things with case: https://github.com/idris-lang/Idris-dev/issues/4001
Because of this, in idris-bi we ultimately had to remove all cases in such functions and define separate top-level helpers that match on the case condition, e.g., like here.
Let's say we have a list : List a, and we want to use its first and second elements:
case inBounds 1 list of
Yes prf => doSmth (index 0 list) (index 1 list)
No _ => doSmthElse
Unfortunately, this does not typecheck: indeed, prf is a proof that InBounds 1 list holds, but while it's obvious to us humans that InBounds 0 list immediately follows, it still needs to be shown to the typechecker.
Of course, one might write a helper proof
inBoundsDecr : InBounds (S k) xs -> InBounds k xs
inBoundsDecr {k = Z} (InLater y) = InFirst
inBoundsDecr {k = (S k)} (InLater y) = InLater (inBoundsDecr y)
and then use it in the Yes branch like this:
case inBounds 1 list of
Yes prf => let prf' = inBoundsDecr prf in
doSmth (index 0 list) (index 1 list)
No _ => doSmthElse
but it seems to be quite verbose.
So, what's the most concise and/or idiomatic way to do this in Idris?
If you have a proof of xs that gives you some information about xs, it is better to use with instead of a case. Then the compiler can see that the argument can be determined by the proof. See this chapter in the tutorial: Views and the "with" rule
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs xs | p = ?hole
Pattern-matching on p gives Yes prf, then Yes (InLater y), then Yes (InLater InFirst), and updating xs to (x :: y :: xs). And so you can easily use x and y:
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs (x :: y :: xs) | (Yes (InLater InFirst)) = x + y
addHs xs | (No contra) = 0
The problem with that is – here it doesn't work properly. If you check if addHs is total, the compiler warns you, because it thinks that another Yes (InLater p) could be reached. Not sure why, the totality checker isn't that great. But in theory it should work fine. :-)
Other than your initial attempt (which may be more verbose, but you don't depend that much on the totality checker), you could of course add a dummy Yes (InLater p) case or stop there and do something like
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs (x :: xs) | (Yes (InLater prf)) = x + (index 0 xs)
addHs xs | (No contra) = 0
Or being somewhat unsafe and assert that the case is unreachable:
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs) proof q
addHs (x :: y :: xs) | Yes (InLater InFirst) = x + y
addHs (x :: xs) | Yes (InLater p) = assert_unreachable
addHs xs | No _ = 0
Following my other question, I tried to implement the actual exercise in Type-Driven Development with Idris for same_cons to prove that, given two equal lists, prepending the same element to each list results in two equal lists.
Example:
prove that 1 :: [1,2,3] == 1 :: [1,2,3]
So I came up with the following code that compiles:
sameS : {xs : List a} -> {ys : List a} -> (x: a) -> xs = ys -> x :: xs = x :: ys
sameS {xs} {ys} x prf = cong prf
same_cons : {xs : List a} -> {ys : List a} -> xs = ys -> x :: xs = x :: ys
same_cons prf = sameS _ prf
I can call it via:
> same_cons {x=5} {xs = [1,2,3]} {ys = [1,2,3]} Refl
Refl : [5, 1, 2, 3] = [5, 1, 2, 3]
Regarding the cong function, my understanding is that it takes a proof, i.e. a = b, but I don't understand its second argument: f a.
> :t cong
cong : (a = b) -> f a = f b
Please explain.
If you have two values u : c and v : c, and a function f : c -> d, then if you know that u = v, it has to follow that f u = f v, following simply from referential transparency.
cong is the proof of the above statement.
In this particular use case, you are setting (via unification) c and d to List a, u to xs, v to ys, and f to (:) x, since you want to prove that xs = ys -> (:) x xs = (:) x ys.
There are times that we want to find an element in a list with a function a -> Bool and replace it using a function a -> a, this may result in a new list:
findr :: (a -> Bool) -> (a -> a) -> [a] -> Maybe [a]
findr _ _ [] = Nothing
findr p f (x:xs)
| p x = Just (f x : xs)
| otherwise = case findr p f xs of Just xs -> Just (x:xs)
_ -> Nothing
Is there any function in the main modules which is similar to this?
Edit: #gallais points out below that you end up only changing the first instance; I thought you were changing every instance.
This is done with break :: (a -> Bool) -> [a] -> ([a], [a]) which gives you the longest prefix which does not satisfy the predicate, followed by the rest of the list.
findr p f list = case break p list of
(xs, y : ys) -> Just (xs ++ f y : ys)
(_, []) -> Nothing
This function is, of course, map, as long as you can combine your predicate function and replacement function the right way.
findr check_f replace_f xs = map (replace_if_needed check_f replace_f) xs
replace_if_needed :: (a -> Bool) -> (a -> a) -> (a -> a)
replace_if_needed check_f replace_f = \x -> if check_f x then replace_f x else x
Now you can do things like findr isAplha toUpper "a123-bc".