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!
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With