【问题标题】:Trouble understanding error handling with monads无法理解 monad 的错误处理
【发布时间】:2015-01-12 19:32:45
【问题描述】:

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

如果列表不包含任何元素,我想返回一个错误。 如果列表正好包含 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

非常感谢任何帮助!

【问题讨论】:

  • 错误信息是什么?始终发布您看到的编译器错误。
  • 对不起,错误很长。我添加了它们并修复了示例。
  • 将类型签名添加到test1,...:可能您希望这些类型为Maybe (Int,[Int]) 或类似的类型。 GHC 为您选择了 Integer 而不是 Int。修复此类型后,您现在可以看到您的函数 singleElemOnly 仅返回 Int/Integer 类型,而它的签名承诺它适用于任何类型 a(不仅适用于 Int)。这是 GHC 报告的类型错误。
  • 此外,我不清楚你的最后一个函数应该做什么。你能编辑你的问题来解释你想要达到的目标吗?我目前的猜测类似于f [] = mzero ; f (x:_) = return x,但很难说。
  • @chi 抱歉,我用最新的代码和错误更新了问题,并更好地解释了我想要实现的目标。

标签: haskell monads monadplus


【解决方案1】:

我会根据你的规范翻译:

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

f [] = mzero

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

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

【讨论】:

  • 这几乎是完美的!但是现在我需要以某种方式将test1 作为参数放入其中,而这部分仍然让我无法理解。 singleElemOnly (fmap snd test2) 不起作用,它抱怨它“无法将类型 Maybe[] 匹配”?
  • singleElemOnly 获取一个列表。 fmap snd test 的类型为 Maybe [Int]。你可以做fmap (singleElement . snd) test,但这会给你一个Maybe (Maybe [Int])。你可能想要fmap snd test >>= singleElement
【解决方案2】:

首先,hoogle 是你的朋友。你可以查签名发现getFirst就是fstgetSecond就是snd,而你对getValue的实现可以写成fromMaybe false

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

同样,如果你在 GHCi 中运行这个,那么 test2 的类型默认为 Maybe (Integer, [Integer]),但 singleElemOnly 必须能够返回 any Integral 类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多