Map then filter in Haskell

Viewed 160

I want to Map an Integer type list in Haskell, and then if the map function returns Nothing I want to discard the result. The function I am trying to write will have a type signature of

mapThenFilter :: (Integer -> Maybe Integer) -> [Integer] -> [Integer]

and so far, I have thought of doing this:

checkIfNothing value = case value of 
     Just a -> a
     Nothing -> 0

mapThenFilter = map checkIfNothing(map f l)

But this is incorrect. I am new to Haskell, so if possible can you advise me on where I am going wrong here? I believe since this is Map then Filter, the Haskell filter function is probably expected to be used too, however, I believe the outer map function in my function definition is doing the job intended(of the filter function)right?

1 Answers

As stated in the comments you are reimplementing mapMaybe.

Which is good. It is a non-obvious function, and it is good that you feel the need for it and came up with that idea, expressed in its type.

If you'd like to do it your way, you were off to a good start actually. Need to tweak it though:

checkIfNothing value = case value of 
     Just a -> a
     Nothing -> 0    

0 is too specific, let's go with []. But now simply returning a won't work. Let's put it in a list, too:

checkIfNothing :: Maybe t -> [t]
checkIfNothing value = case value of 
     Just a -> [a]
     Nothing -> []    

So that now,

mapThenFilter f l  =   map checkIfNothing (map f l)

is almost right. We produce our results in lists, and those that were skipped result in empty lists:

   -- instead of
   [ a,   b,  c, .... , z ]
   -- we produce
   [ [], [b], [], .... , [z] ]

and so we just need to concatenate them, to join them together with ++s:

     []++[b]++[]++...,++[z]  

   [      b,       ...., z   ]

And so we define

mapThenFilter :: (a1 -> Maybe a) -> [a1] -> [a]
mapThenFilter f l  =  concat $ map checkIfNothing (map f l)

By the way checkIfNothing also already exists. It is known as maybeToList:

> foo f l = concat $ map maybeToList $ map f l
foo :: (a1 -> Maybe a) -> [a1] -> [a]

> foo (\x -> listToMaybe [x | even x]) [1..10]
[2,4,6,8,10]

A combination of concat and map is important enough to have a special function assigned to do its job, concatMap. And that function is important enough to even be assigned a special operator, to do its job:

foo f = concat . map maybeToList . map f  
      = concat . map (maybeToList . f)
      = concatMap (maybeToList . f)
      = (maybeToList . f  =<<)
Related