0

Got stuck again while trying to learn some Haskell. What I'm trying to do is implementing a combined head/tail method for lists with error handling. The signature must look like this:

head' :: MonadPlus m => [a] -> m (a,[a])

However I'm a bit lost at how error handling with MonadPlus works. I tried the following:

head' xs = if (length xs > 0) then Just(head xs, tail xs) else Nothing

but it complains: Expected type: m (a, [a]) Actual type: Maybe (a, [a]). Just for fun I also tried:

head' xs = if (length xs > 0) then Just(head xs, tail xs) else Nothing `mplus` Nothing

but not only does that look redundant, it also does not work either.

Any hints appreciated!

duplode
  • 33,731
  • 7
  • 79
  • 150
Marco
  • 1,430
  • 10
  • 18
  • Maybe is one possible MonadPlus, there are many others. If you want to work with any possible MonadPlus, don't use Maybe functions, they are too specific. – n. m. could be an AI Jan 11 '15 at 17:35

1 Answers1

3

Try this:

head' :: MonadPlus m => [a] -> m (a,[a])
head' xs = if null xs then mzero then return (head xs, tail xs)

mzero is the "nothing" or (indeed) "zero" value, which you can use here to model the absence of a result. It has type m x for any type x and monad with zero m, so it fits here.

Instead, return wraps any value in any monad. In particular, you can insert our (a,[a]) pair inside m. (This does not even require the monad to be a MonadPlus)

Note how in the code above we did not mention any specific monad: the code is indeed generic, so that it can work with any MonadPlus.

Lastly, I used null instead of length ... > 0 since null only examines the first constructor in the list, while length ... > 0 has to traverse it all.

You can actually remove the check by switching to pattern matching:

head' :: MonadPlus m => [a] -> m (a,[a])
head' []     = mzero
head' (x:xs) = return (x, xs)
chi
  • 111,837
  • 3
  • 133
  • 218
  • After I fixed the error in the above if-statement, the code compiled. However, when trying to run it, e.g. by calling `head' []`, I get: No instance for (MonadPlus m0) arising from a use of 'it' The type variable 'm0' is ambiguous Note: there are several potential instances: instance MonadPlus Maybe -- Defined in 'Control.Monad' instance MonadPlus [] -- Defined in 'Control.Monad' In the first argument of 'print', namely 'it' – Marco Jan 11 '15 at 17:57
  • 2
    @Marco When you use a polymorphic type, you have to specify the concrete type at which you want to use it. This is done e.g. by writing `head' [] :: Maybe (Int, [Int])` or by using it in a suitable context e.g. `f (head' [])` where `f` has type `Maybe (Int,[Int]) -> ...`. Of course, instead of `Maybe` you can use any other `MonadPlus`, and instead of `Int` anything else. The same issue is found with `read "32"`, for instance. – chi Jan 11 '15 at 18:29