如何在 Haskell 中使用 Monad 类将函数映射到多个构造函数参数上?

λ-d*_*tox 4 monads haskell functional-programming

我偶然发现的问题与>>=应用到这样的样本类型有关:

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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

(ThreeArgs a b c) >>= f = f a
Run Code Online (Sandbox Code Playgroud)

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

*module1> let x = do { x <- ThreeArgs 1 2 3; y <- ThreeArgs 4 6 7; return $ x + y }
*module1> x
ThreeArgs 5 5 5 
Run Code Online (Sandbox Code Playgroud)

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

(ThreeArgs a b c) >>= f = (f a) (f b) (f c)
Run Code Online (Sandbox Code Playgroud)

编译器警告:

 Couldn't match expected type `ThreeArgs b
                                -> ThreeArgs b -> ThreeArgs b -> ThreeArgs b'
              with actual type `ThreeArgs b'
Run Code Online (Sandbox Code Playgroud)

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

Jos*_*ica 7

ThreeArgs同构于((->) 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)
Run Code Online (Sandbox Code Playgroud)

FunctorApplicative实例如何为的名称相符((->) r)的工作,所以我们就可以让它搭配怎样Monad一个工作也是一样,我们就大功告成了。

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
Run Code Online (Sandbox Code Playgroud)

顺便说一句,ThreeArgs如果您想查找更多关于此的信息,则数据结构的通用术语是“可表示函子”。