1

我正在尝试构建一个函数,它返回列表中的单个元素。该列表是元组的一部分Maybe (Int,[Int])

如果列表不包含任何元素,我想返回一个错误。如果列表正好包含 1 个元素,我想将该元素作为 Monad 返回。如果列表包含超过 1 个元素,我想返回一个错误。

我有点迷茫,看不出如何让这个相当简单的事情发挥作用。这是我到目前为止所拥有的:

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

不幸的是,我在尝试编译时收到的错误消息对我作为初学者来说绝对没有用..

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

非常感谢任何帮助!

4

2 回答 2

2

我将从您的规范中翻译:

如果列表不包含任何元素,我想返回一个错误。

f [] = mzero

如果列表正好包含 1 个元素,我想将该元素作为 Monad 返回。

f [x] = return x

如果列表包含超过 1 个元素,我想返回一个错误。

f (_:_:_) = mzero

所以,把所有东西放在一起:

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

或者更简单地说,因为第三种情况包括第一种情况:

singleElemOnly :: (MonadPlus m) => [a] -> m a
singleElemOnly [x] = return x
singleElemOnly _   = mzero
于 2015-01-12T20:05:22.487 回答
1

首先,hoogle是你的朋友。您可以查找签名并发现getFirstis just fst, getSecondis snd,并且您的实现getValue可以写为fromMaybe false.

就错误消息而言,value具有类型(Integral i) => Maybe i(或类似的东西,我现在没有编译器),这是singleElemOnly返回的一个可能值。但是签名(MonadPlus m) => [a] -> m a说它必须返回调用者想要的任何东西。 MonadPlus

同样,如果您在 GHCi 中运行它,则类型test2默认为Maybe (Integer, [Integer]),但singleElemOnly必须能够返回任何 Integral类型。

于 2015-01-12T19:44:53.970 回答