Marco
Marco

Reputation: 1460

Trouble understanding error handling with monads

I'm trying to build a function which returns me the single element from a list. The list is part of a Maybe (Int,[Int]) tupel.

If the list contains no elements, I want to return an error. If the list contains exactly 1 element, I want to return that element as a Monad. If the list contains more than 1 element, I want to return an error.

I'm a bit lost and cannot see how to make this rather simple thing work. Here is what I have so far:

import Control.Monad

test1 = Just (1,[2,3]) :: Maybe (Int,[Int])
test2 = Just (2,[1]) :: Maybe (Int,[Int])
test3 = Just (3,[]) :: Maybe (Int,[Int])

getValue :: Maybe Bool -> Bool
getValue (Just x) = x
getValue Nothing = False

singleElemOnly :: (MonadPlus m) => [a] -> m a
singleElemOnly x = let result = test2
                       value = fmap fst result
                       isEmpty = fmap null (fmap snd result)
                   in if (getValue isEmpty) then value else mzero

Unfortunately the error messages I am getting when trying to compile this are of absolutely no use to me as a beginner..

Playground.hs:15:50:
    Could not deduce (a ~ Int)
    from the context (MonadPlus m)
      bound by the type signature for
                 singleElemOnly :: MonadPlus m => [a] -> m a
      at Playground.hs:11:19-45
      `a' is a rigid type variable bound by
          the type signature for singleElemOnly :: MonadPlus m => [a] -> m a
          at Playground.hs:11:19
    Expected type: m a
      Actual type: Maybe Int
    Relevant bindings include
      x :: [a]
        (bound at Playground.hs:12:16)
      singleElemOnly :: [a] -> m a
        (bound at Playground.hs:12:1)
    In the expression: value
    In the expression: if (getValue isEmpty) then value else mzero

Playground.hs:15:50:
    Could not deduce (m ~ Maybe)
    from the context (MonadPlus m)
      bound by the type signature for
                 singleElemOnly :: MonadPlus m => [a] -> m a
      at Playground.hs:11:19-45
      `m' is a rigid type variable bound by
          the type signature for singleElemOnly :: MonadPlus m => [a] -> m a
          at Playground.hs:11:19
    Expected type: m a
      Actual type: Maybe Int
    Relevant bindings include
      singleElemOnly :: [a] -> m a
        (bound at Playground.hs:12:1)
    In the expression: value
    In the expression: if (getValue isEmpty) then value else mzero

Any help much appreciated!

Upvotes: 1

Views: 101

Answers (2)

chi
chi

Reputation: 116139

I will translate from your specification:

If the list contains no elements, I want to return an error.

f [] = mzero

If the list contains exactly 1 element, I want to return that element as a Monad.

f [x] = return x

If the list contains more than 1 element, I want to return an error.

f (_:_:_) = mzero

So, putting everything together:

singleElemOnly :: (MonadPlus m) => [a] -> m a
singleElemOnly []  = mzero
singleElemOnly [x] = return x
singleElemOnly _   = mzero
       -- a _ catches everything else, no need to write the exact pattern

Or even more simply, since the third case includes the first:

singleElemOnly :: (MonadPlus m) => [a] -> m a
singleElemOnly [x] = return x
singleElemOnly _   = mzero

Upvotes: 2

crockeea
crockeea

Reputation: 21811

First, hoogle is your friend. You can look up signatures and find that getFirst is just fst, getSecond is snd, and your implementation of getValue can be written as fromMaybe false.

In terms of the error messages, value has the type (Integral i) => Maybe i (or something close, I don't have a compiler right now), which is one possible value that singleElemOnly returns. But the signature (MonadPlus m) => [a] -> m a says that it must return any MonadPlus that the caller wants.

Similarly, if you are running this in GHCi, then the type of test2 defaults to Maybe (Integer, [Integer]), but singleElemOnly must be able to return any Integral type.

Upvotes: 1

Related Questions