
El mié, 02-03-2011 a las 10:04 -0500, Patrick Lynch escribió:
I think my problem that I'm facing is that I don't see how the instance Functor Maybe is derived from the class Functor...
I don't see how the instance Functor Maybe is derived [presumably: 'f' is function (a->b) and not functor 'f' as shown in the class description): instance Functor Maybe where fmap f (Just x) = Just (f x) fmap f Nothing = Nothing
Perhaps it'll serve you well to think of the monad as a context for computation. In the case of Maybe, it allows you to represent failure in your computations. Warning: lengthy mail piece ahead! Suppose you've build a chain of computations, each segregated in its own function. Some of those functions can fail, some others not. In a different language you might use a special value to represent failure: an empty string, an emtpy list, zero, some language-specific contruct a-la NULL, etc. That approach has its cons, though: you have to check for that particular value in each link of your chain, and since this fail-value is tied to the return type, different special values will be used throughout your chain... which is cumbersome, and confusing. The Maybe type allows to represent failure through a much cleaner (smarter) mechanism: You build your chain of computations in the Maybe type. By using it as a monad, successful links will wrap its return value in 'Just', and whichever link in your chain failing will return 'Nothing' (regardless of what it's working on, be it strings or numbers or what not). As a result, your whole chain will short-circuit at that failing link. The rest of the computations are never run. As an example, suppose you've built a function 'fn', which type is 'a' -> 'b', and your chain of functions so far gives you that 'a'... But some of the functions running before 'fn' may fail (signaling, for example, invalid input). Instead of choosing a particular value 'a' to signal that error, you decide to wrap your functions in Maybe, and use Maybe as a monad. Now 'fn' will recieve a 'Maybe a'... Assuming 'fn' never fails (a situation quite common in long chains), you don't need to return a 'Maybe b', so: fn :: Maybe a -> b fn Nothing = someValueNeverUsed fn Just someValue = fn someValue And rely in previous computations to sanitaze fn's input .But, what if links running _after_ wrapFN can also fail? They live in the Maybe monad too. So, it can be advantageous to return the results of 'fn' in Maybe, even if 'fn' will never fail: wrapFN :: Maybe a -> Maybe b wrapFN ma = do res <- ma case res of Nothing -> Nothing Just bs -> Just $ fn bs You see, we run action 'ma' and act according to its results. If 'ma' returns Nothing, we'll feed the next link in the chain Nothing, propagating failure. Notice that, in this case 'fn' is never used, never runs. This saves resources. Assuming all other links in the chain follow a similar bahaviour, your chain now short-circuits on failure. Hurra! But really, what a bummer, wrappers everywhere? Enter 'fmap' : wrapFN :: Maybe a -> Maybe b wrapFN ma = fn `fmap` ma -- or in point free: wrapFN = fmap fn Which provides the same functionality. If before you where doing something like: maybeActionA >>= maybeActionB >>= wrapperFN >>= maybeActionC now you do: maybeActionA >>= maybeActionB >>= fmap fn >>= maybeActionC Which shows how you've "lifted" the function 'fn' into the Maybe monad, with little effort thanks to 'fmap'. In order for the above to work, you need the Functor Maybe instance. Review it now, see if it makes more sense: instance Functor Maybe where fmap f (Just x) = Just (f x) -- wrap successful computation fmap f Nothing = Nothing -- propagate failure