Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell working with Maybe lists

I'm pretty new to haskell and don't get how to work with Maybe [a]. Normally I'm coding OOP (VB.NET) and in my free time I want to learn haskell (don't ask why ;) ).

So, what do I want to do? I want to read two files with numerical IDs and find out only the IDs that match in both files. Reading the files is not a big thing, it works wonderfully easy. Now, I get two lists of Maybe[Ids] (for a simple example, just think the IDs are Int). So the function I need looks like this

playWithMaybe :: (Maybe [a]) -> (Maybe [Int]) -> [Int]

Now I want to access list members as I used to like this

playWithMaybe (x:xs) (y:ys) = undefined

But unfortunately it's not allowed, GHC says for both lists

Couldn't match expected type ‘Maybe [Int]’ with actual type ‘[t0]’

So I played around a little bit but didn't find a way to access list members. Can someone help me out? A little bit of explanation would be a great thing!

like image 891
sandkasten Avatar asked Jun 23 '16 05:06

sandkasten


People also ask

How do you deal with maybe Haskell?

1) Just: Just represent if the value is present inside the value of type maybe. If it found it true then it will return us the value. 2) Nothing: Nothing represents if the value is not present inside the value of type maybe. If it is not able to find the value then it will return us the default value or return nothing.

What is maybe type in Haskell?

The Maybe type encapsulates an optional value. A value of type Maybe a either contains a value of type a (represented as Just a), or it is empty (represented as Nothing). Using Maybe is a good way to deal with errors or exceptional cases without resorting to drastic measures such as error.

Is maybe an instance of Ord?

As one can see from the type definition, Maybe will be an instance of Eq and Ord when the base type is. As well, instances of Functor and Monad are defined for Maybe.


1 Answers

To approach your problem from a different direction, I would argue you don't want a function that processes two Maybe [a]. Bear with me:

Fundamentally, the operation you want to do operates on two lists to give you a new list, eg,

yourFunction :: [a] -> [a] -> [a]
yourFunction a b = ...

That's fine, and you can and should write yourFunction as such. The fact that the data you have is Maybe [a] captures some additional, auxiliary information: the operation that created your input lists may have failed. The next step is to chain together yourFunction with the auxiliary information. This is exactly the purpose of do notation, to mix pure operations (like yourFunction) with contexts (the fact that the creation of one of your input lists may have failed):

playWithMaybe :: Maybe [a] -> Maybe [a] -> Maybe [a]
playWithMaybe maybeA maybeB =
  do a <- maybeA   -- if A is something, get it; otherwise, pass through Nothing
     b <- maybeB   -- if B is something, get it; otherwise, pass through Nothing
     Just (yourFunction a b)  -- both inputs succeeded!  do the operation, and return the result

But then it turns out there are other kinds of contexts you might want to work with (a simple one, instead of Maybe that just captures "something bad happened", we can use Either to capture "something bad happened, and here is a description of what happened). Looking back at playWithMaybe, the "Maybe-ness" only shows up in one place, the Just in the last line. It turns out Haskell offers a generic function pure to wrap a pure value, like what we get from yourFunction, in a minimal context:

playWithMaybe' :: Maybe [a] -> Maybe [a] -> Maybe [a]
playWithMaybe' maybeA maybeB =
  do a <- maybeA
     b <- maybeB
     pure (yourFunction a b)

But then Haskell also has a generic type to abstract the idea of a context, the Monad. This lets us make our function even more generic:

playWithMonad :: Monad m => m [a] -> m [a] -> m [a]
playWithMonad mA mB =
  do a <- mA
     b <- mB
     pure (yourFunction a b)

Now we have something very generic, and it turns out it is so generic, it's already in the standard library! (This is getting quite subtle, so don't worry if it doesn't all make sense yet.)

import Control.Applicative
play :: Monad m => m [a] -> m [a] -> m [a]
play mA mB = liftA2 yourFunction mA mB

or even

import Control.Applicative
play' :: Monad m => m [a] -> m [a] -> m [a]
play' = liftA2 yourFunction

Why did I switch from Monad to Applicative suddenly? Applicative is similar to Monad, but even more generic, so given the choice, it is generally better to use Applicative if you can (similar to my choice to use pure instead of return earlier). For a more complete explanation, I strongly recommend Learn You a Haskell (http://learnyouahaskell.com/chapters), in particular chapters 11 and 12. Note- definitely read chapter 11 first! Monads only makes sense after you have a grasp on Functor and Applicative.

like image 88
Matt Avatar answered Oct 13 '22 00:10

Matt