如果我像这样定义“bind”函数:
(>>=) :: M a -> (a -> M' b) -> M' b
如果我希望结果是新的Monad类型,或者我应该使用相同的Monad但是在同一个Monad框中使用b,那么这个定义会帮助我吗?
答案 0 :(得分:7)
正如我在评论中提到的,我不认为可以为一般的monad安全地定义这样的操作(例如M = IO
,M' = Maybe
)。
但是,如果M可以安全地转换为M',则此绑定可以定义为:
convert :: M1 a -> M2 a
...
(>>=*) :: M1 a -> (a -> M2 b) -> M2 b
x >>=* f = convert x >>= f
相反,
convert x = x >>=* return
一些此类安全转换方法为maybeToList
(可能→[]),listToMaybe
([]→可能),stToIO
(ST RealWorld→IO),...注意对于任何monad都没有通用的convert
方法。
答案 1 :(得分:1)
这个定义不仅没有帮助,而且会严重混淆未来的代码读者,因为它会打破使用它的所有期望。
例如,M和M'都应该是Monads吗?如果是这样,那么它们是如何定义的?请记住:>>=
的定义是Monad定义的一部分,并且在任何地方用于定义其他使用Monad的函数 - 除了return
和fail
之外的每个函数。
另外,您可以选择使用哪种M和M',还是选择计算机?如果是这样,那么你如何选择?它是否适用于任何两个Monad实例,或者是否有你想要的Monad子集 - 或者M的选择是否决定M的选择?
可以创建一个像你所写的一样的函数,但肯定是很多比>>=
更复杂,并且它会产生误导,残忍,并且可能是灾难性的。试着把你的功能塞进>>=
的衣服里。
答案 2 :(得分:1)
这可能是一件复杂的事情,但在某些情况下它是可行的。基本上,如果它们是monad你可以在里面看到(例如Maybe
或你写过的monad),那么你可以定义这样的操作。
有时候(在GHC中)非常方便的一件事就是用你自己的一个替换Monad
类。如果您定义return, >>=, fail
,您仍然可以使用do
表示法。这是一个可能与您想要的一样的例子:
class Compose s t where
type Comp s t
class Monad m where
return :: a -> m s a
fail :: String -> m a
(>>=) :: (Compose s t) => m s a -> (a -> m t b) -> m (Comp s t) b
(>>) :: (Compose s t) => m s a -> m t b -> m (Comp s t) b
m >> m' = m >>= \_ -> m'
然后,您可以根据您定义的Compose
实例,使用绑定运算符控制可以对哪些类型进行排序。当然,你经常需要Comp s s = s
,但你也可以用它来定义各种疯狂的东西。
例如,也许你的monad中有一些操作,绝对不能跟随任何其他操作。想要静态执行吗?定义空数据类型data Terminal
,不提供Compose Terminal t
的实例。
这种方法不适合从(例如)Maybe
转换为IO
,但它可用于传输有关您正在做的事情的某些类型级数据。
如果您确实想要更改monad,可以将上面的类定义修改为
class Compose m n where
type Comp m n
(>>=*) :: m a -> (a -> n b) -> (Compose m n) b
class Monad m where
return :: a -> m a
fail :: String -> m a
(>>=) :: Compose m n => m a -> (a -> n b) -> (Compose m n) b
m >>= f = m >>=* f
(>>) :: Compose m n => m a -> (n b) -> (Compose m n) b
m >> n = m >>=* \_ -> n
我已经将前一种风格用于有用的目的,尽管我认为后一种观点在某些情况下也可能有用。
答案 3 :(得分:0)
您可能需要查看Oleg的这个示例:http://okmij.org/ftp/Computation/monads.html#param-monad