This was a challenge from a book: filter out Rights from a list of [Either String Integer].
This works just fine
lst1 :: [Either String Integer]
lst1 = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]
lefts' :: [Either a b] -> [a]
lefts' [] = []
lefts' (x:xs) = if isLeft x
then (\(Left z) -> z) x : lefts' xs
else lefts' xs
But I wanted to write the lambda as a separate function and could not figure out how to do it. I tried:
getLeftVal :: Either a b -> a
getLeftVal (Left l) = l
But no surprise it gives the pattern matches not exhaustive warning.
I could of course do:
getLeftVal :: Either a b -> a
getLeftVal x = (\(Left z) -> z) x
Your original lambda based implementation also gives a warning on newer GHC versions. The warning is under a separate flag -Wincomplete-uni-patterns which is included in -Wall only on newer compiler versions (I believe from GHC 9 on).
The reason is that GHC is not smart enough to look into the definition of the isLeft function. That would be very costly to do everywhere.
Instead, as Tom suggests, you should use pattern matching in the lefts' function so that the compiler can give more accurate error messages.
That would look like this by the way:
lefts' (x:xs) = case x of
Left x' -> x' : lefts' xs
Right _ -> lefts' xs
I thought about a list comprehension but would not have know to write it like that. Is the Left x to the left of <- l a pattern match that just ignores Right x?
The reason I didn’t do that was because of what Graham Hutton said in one of his FP lectures on youtube. Namely that explicitly matching on Right makes the patterns independent in the sense that you can change the order of the lines without changing the meaning of the function. Also, when reading the function you can read and understand each line separately.
I don’t understand why they’re so often shown to beginners, and this is the main reason why. You can sort of imagine what’s going on in terms of map and filter in simple cases, but to understand everything about how they work, you need to grok the Monad instance for [].
Looks like “almost” was 99.5%. Just remove ... unless I’m missing something.
keepLefts :: Either a b1 -> [Either a b2] -> [Either a b2]
keepLefts (Left x) xs = Left x : xs
keepLefts (Right _) xs = xs
lefts' :: Foldable t => t (Either String Integer) -> [Either String Integer]
lefts' = foldr keepLefts []
Also, I haven’t thought about this before, but it looks like in this case having b, b1 & b2 is unnecessary since we know b will always be an Integer. is
keepLefts :: Either a b1 -> [Either a b2] -> [Either a b2]
more correct in some way than
keepLefts :: Either a b -> [Either a b] -> [Either a b]