Function to get value of a Left

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

But is redundant and extra code.

I tried a few other things but none worked.

The traditional solution is to use a pattern match and not a boolean isConstructor test.

3 Likes

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

Or

lefts' (Left x:xs) = x : lefts' xs
lefts' (Right _:xs) = lefts' xs
4 Likes

Since lists are being used, use comprehensions instead of lambda-definitions:

lefts' :: [Either a b] -> [a]
lefts' l = [ x | Left x <- l ]

Simplicity has its benefits…

2 Likes

I always found list comprehension magical :smiley:

2 Likes

I was trying to find a way to detect a Left without having to pattern match Right. Seems that was a wrong turn. I like your second more.

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?

Beginners have a way of making things complicated :slight_smile:

1 Like

You can do it without matching on Right:

lefts' (Left x:xs) = x : lefts' xs
lefts' (_:xs) = lefts' xs

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.

2 Likes

Correct. See section 3.11 (page 42 of 329) in the Haskell 2010 Report for more details.

1 Like

The book wanted a solution using foldr so I came up with

lst1 :: [Either String Integer]
lst1 = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]

isItLeft :: Either a b1 -> [Either a b2]
isItLeft (Left x) = [Left x]
isItLeft (Right x) = []

lefts' :: Foldable t => t (Either a b1) -> [Either a b2]
lefts' xs = foldr (\cur acc -> acc ++ isItLeft cur) [] xs


ghci> lefts' lst1
[Left "baz",Left "bar",Left "foo"]

Hint:

keepLefts (Left x)  = (x:)
keepLefts (Right _) = ...

Also look at the either function…

Wouldn’t keepLefts (Left x) = (x:) return the value from inside the Left instead of a Either/Left?

Yes, just as this would do:

Keeping the Left constructors only requires a small change:

keepLefts (Left x)  = (Left x :)
keepLefts (Right _) = ...

or using an as-pattern:

keepLefts x@(Left _)  = (x:)
keepLefts (Right _) = ...
1 Like

So I struggled with that a bit until I change (Left x :) to ([Left x] :). And now this works:

lst1 :: [Either String Integer]
lst1 = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]

keepLefts :: Either a b1 -> [[Either a b2]] -> [[Either a b2]]
keepLefts (Left x)  = ([Left x] :)
keepLefts (Right _) = ([] :)


lefts' :: Foldable t => t (Either String Integer) -> [[Either String Integer]]
lefts' xs = foldr (\cur acc -> keepLefts cur acc ) [] xs

From you example I also realized I was reversing the order of the list so am using : instead of ++.

And if I had to say what keepLefts is returning: "A partial function from [[Either a b2]] -> [[Either a b2]]". Is that right?

You’ve simplified the lambda-function here:

…to the point where cur and acc are now redundant:

lefts' xs = foldr keepLefts [] xs  -- after eta-reduction!

…let’s now rewrite my fragment of keepLefts in similar fashion - I was using a cunningly-disguised lambda-function called a section:

keepLefts (Left x)  = (\xs -> Left x : xs)
keepLefts (Right _) = ...         

After the rewrite:

keepLefts (Left x)  xs = Left x : xs
keepLefts (Right _) xs = ... xs

As you can see, the lambda-parameter xs is now a parameter of keepLefts and is in its result no matter what:

= Left x : xs  -- for a (Left ...) value
= ... xs       -- for a (Right ...) value

So (in this case!) keepLefts shouldn’t be partial - in either case, it returns a useful result.

Bringing it (almost ;-) altogether:

lst1 :: [Either String Integer]
lst1 = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]

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' xs = foldr keepLefts [] xs
1 Like

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 [].

3 Likes

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]

Which works equally well?

Yes - it’s more generic:

ghci> :t keepLefts
keepLefts :: Either a b1 -> [Either a b2] -> [Either a b2]
ghci> 

When keepLefts is used, GHC will specialise the type as needed.

1 Like

To close out a dangling line of thought, you can also spell keepLefts as

keepLefts :: Either a b -> [a] -> [a]
keepLefts = either (:) (flip const) -- or ((:) . Left) if you want to preserve the Lefts, for some reason