Reputation: 1460
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
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
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