MarkusAnd
MarkusAnd

Reputation: 812

How to recursively iterate over a list with Maybe objects in Haskell

I am trying to create a function justifyList that takes a list of Maybe objects ([Maybe a]) and return a list of all objects of Just type, discarding any element that is Nothing in the parameter list.

My solution for now is

justifyList :: [Maybe a] -> [a]
justifyList [] = []
justifyList (x:xs) 
    | (isNothing x) = justifyList xs
    | otherwise = x : justifyList xs

where I try to recursively iterate through the parameter list and recursively put the current element x to the returned list [a], if it is of Just type.

However when interpreting a type mismatch error is generated

Couldn't match type a with Maybe a

Hence my question is; how do I recursively iterate through a list of Maybe objects? If this solution is sufficient, how do I convert xs to be of Maybe type?

Upvotes: 4

Views: 981

Answers (4)

chepner
chepner

Reputation: 532508

If the first element of the list is Nothing, apply id to the recursive result. If it is Just x, apply (x:) to the recursive result. Use maybe to select the correct function.

justifyList [] = []
justifyList (x:xs) = (maybe id (:) x) (justifyList xs)
  • maybe id (:) Nothing == id
  • maybe id (:) (Just x) == (x:).

Upvotes: 1

John F. Miller
John F. Miller

Reputation: 27225

In the line justifyList (x:xs) x is of type Maybe a then in the line | otherwise = x : justifyList xs you try to add x::Maybe a to a list of a.

You need to break open the Maybe type using pattern matching so you can get the actual value inside. (I am going to use an explicit case statement because it makes the logic easier but best practice is to use pattern matching in the function itself - left as an exercise to the reader).

justifyList (x:xs) = case x of
  Nothing -> justifyList xs
  Just a  -> a:justifyList xs

As a general rule only use a guard pattern (| = ...) when you need to makes some test like a comparison or function evaluation. When all you need to is some information about the type (which is much of the time in practice) use pattern matching or case statements instead.


For those who may find this in a search I will add one last bit there is a function catMaybes in Data.Maybe which does exactly this operation already.

Upvotes: 3

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477854

Such function already lives in the Data.Maybe module and is named catMaybes :: [Maybe a] -> [a].

You can implement this with recursion, but it might be more elegant to do this with list comprehension, since this allows us to effectively pattern match and unwrap at the same time, so:

justifyList :: [Maybe a] -> [a]
justifyList xs = [ x | Just x <- xs ]

only for items that satisfy the Just x pattern, it will yield an item, so Just x <- xs performs a filtering as well.

Upvotes: 10

Fyodor Soikin
Fyodor Soikin

Reputation: 80915

Pattern-match on the Maybes

justifyList :: [Maybe a] -> [a]
justifyList [] = []
justifyList (Nothing:xs) = justifyList xs
justifyList (Just x:xs) = x : justifyList xs

Upvotes: 7

Related Questions