【问题标题】:How to map a function over multiple constructor arguments working with Monad class in Haskell?如何将函数映射到使用 Haskell 中的 Monad 类的多个构造函数参数上?
【发布时间】:2020-04-23 11:16:15
【问题描述】:

我偶然发现的问题与 >>= 应用程序对这样的示例类型有关:

data ThreeArgs a = ThreeArgs a a a deriving (Show,Eq)

instance Functor ThreeArgs where
    fmap f (ThreeArgs a b c) = ThreeArgs (f a) (f b) (f c)

instance Applicative ThreeArgs where
    pure x = ThreeArgs x x x
    (ThreeArgs a b c) <*> (ThreeArgs p s q) = ThreeArgs (a p) (b s) (c q)

我会声明一个 Monad 实例如下:

instance Monad ThreeArgs where
    return x = ThreeArgs x x x
    (ThreeArgs a b c) >>= f = f ... -- a code I need to complete

是的,看起来f 似乎应用于所有三个ThreeArgs 构造函数参数。如果我完成最后一行

(ThreeArgs a b c) >>= f = f a

那么编译器没有任何抱怨,而结果是:

*module1> let x = do { x <- ThreeArgs 1 2 3; y <- ThreeArgs 4 6 7; return $ x + y }
*module1> x
ThreeArgs 5 5 5 

这意味着求和会导致具有相同参数值的上下文,尽管正确的输出应该是ThreeArgs 5 8 10。一旦我编辑到

(ThreeArgs a b c) >>= f = (f a) (f b) (f c)

编译器警报:

 Couldn't match expected type `ThreeArgs b
                                -> ThreeArgs b -> ThreeArgs b -> ThreeArgs b'
              with actual type `ThreeArgs b'

所以,我看到一个严重的错误指导了我的理解,但我仍然很难理解 Haskell 中的单子类和其他类似的东西。大概,我想在这里使用递归还是其他什么?

【问题讨论】:

  • 我不能肯定地说,但我的直觉是没有“明智的” Monad 实例(一个使用所有 3 个包含值的实例,特别是一个给出您打算用于该示例的结果的实例)这种类型是可能的。您对类型应该代表什么有任何见解吗?您是否已经创建了 FunctorApplicative 实例?
  • 我已经为 Functor 和 Applicative 的实例添加了一些代码。要管理这个问题,以便在输入do { x &lt;- ThreeArgs 1 2 3; y &lt;- ThreeArgs 4 6 7; return $ x + y } 后程序运行到ThreeArgs 5 8 10
  • 谢谢,这些实例符合我的预期,但只是想确认一下。我认为这里没有 Monad 实例的原因是您的类型现在基本上是 ZipList 限制为只有 3 个元素。 ZipList 没有 Monad 实例(尽管我不知道如何证明永远不可能存在)。此外,我希望将元素限制在 3 个以内会使实例变得更加困难,而不是更容易。
  • @tex 但是所有三个值的类型都是aFunctor 实例很好。 fmap id (ThreeArgs a b c) == ThreeArgs a b cfmap (f . g) (ThreeArgs a b c) == ThreeArgs (f (g a)) (f (g b)) (f (g c)) == fmap f (ThreeArgs (g a) (g b) (g c))
  • @RobinZigmond 实际上,限制确实让事情变得更容易,因为它将不可能变为可能。

标签: haskell functional-programming monads


【解决方案1】:

ThreeArgs((-&gt;) Ordering) 同构。证人:

to :: ThreeArgs a -> Ordering -> a
to (ThreeArgs x _ _) LT = x
to (ThreeArgs _ y _) EQ = y
to (ThreeArgs _ _ z) GT = z

from :: (Ordering -> a) -> ThreeArgs a
from f = ThreeArgs (f LT) (f EQ) (f GT)

您的FunctorApplicative 实例与((-&gt;) r) 的工作方式相匹配,因此我们只需使其与its Monad one 的工作方式相匹配,我们就完成了。

instance Monad ThreeArgs where
    ThreeArgs x y z >>= f = ThreeArgs x' y' z' where
        ThreeArgs x' _ _ = f x
        ThreeArgs _ y' _ = f y
        ThreeArgs _ _ z' = f z

顺便说一下,像ThreeArgs 这样的数据结构的通用术语是“可表示函子”,如果您想了解更多相关信息。

【讨论】:

  • 这是一个很好的答案,因为您的解释让我了解了一些关于 monad 的基本知识。积极地,我已经考虑过跳过 Monad 实例的任何选项,但必须寻求涉及该实例的解决方案。事情被很好地聚焦了,谢谢。
猜你喜欢
  • 1970-01-01
  • 2012-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
  • 2020-05-16
  • 1970-01-01
相关资源
最近更新 更多