单个monad的多个flatMap方法?

时间:2013-05-06 21:12:24

标签: scala haskell monads

在Monad中定义多个flatMap(或Haskell中的>>= / bind)方法是否有意义? 我实际使用的极少数monad(OptionTryEither投影)只定义了一个flatMap方法。

例如,在flatMap上定义一个Option方法是否有意义,它会产生一个Try的函数?那么Option[Try[User]]会被展平为Option[User]例如? (考虑失去异常不是问题......)

或者monad应该只定义一个flatMap方法,使用一个生成相同类型monad的函数?我想在这种情况下Either投影不会是monad?是吗?

4 个答案:

答案 0 :(得分:5)

我曾经认真考虑过这件事。事实证明,这样的构造(除了失去所有monadic功能)并不是很有趣,因为它足以提供从内部到外部容器的转换:

joinWith :: (Functor m, Monad m) => (n a -> m a) -> m (n a) -> m a
joinWith i = join . (fmap i)

bindWith :: (Functor m, Monad m) => (n a -> m a) -> m a -> (a -> n a) -> m a
bindWith i x f = joinWith i $ fmap f x

*Main>  let maybeToList = (\x -> case x of Nothing -> []; (Just y) -> [y])
*Main>  bindWith maybeToList [1..9] (\x -> if even x then Just x else Nothing)
[2,4,6,8]

答案 1 :(得分:1)

这取决于“有意义”的意思。

如果你的意思是它与monad法则一致,那么对我来说这个问题完全没有意义。我必须看到一个具体的提议。如果你按照我认为的方式进行,你可能最终会在某些极端情况下违反构图。

如果你的意思是有用,当然,你总能找到这些事情有用的案例。问题在于,如果你开始违反monad法则,你就会在代码中留下陷阱,用于粗暴的功能(类别理论)推理器。更好地制作看起来像monad的东西实际上是monad(并且一次只有一个,尽管你可以提供一种明确的方式来切换la Either - 但是你写的是正确的{{1严格来说,并且LeftProjection不是monad)。或者写一些非常清晰的文档来解释它不是它的样子。否则,如果法律成立,有人会愉快地继续,并且* splat *。

答案 2 :(得分:1)

对于特定的数据类型,它没有意义,据我所知,您只能有bind的一个定义。

在haskell中,monad是以下类型类,

instance Monad m where  
    return :: a -> m a
    bind   :: m a -> (a -> m b) -> m b

具体来说,我们有Monad列表,

instance Monad [] where
    return :: a -> [] a
    (>>=)   :: [] a -> (a -> [] b) -> [] b

现在让我们考虑一个monadic函数。

actOnList :: a -> [] b 
 ....

一个用例来说明,

$ [1,2,3] >>= actOnList

在函数actOnList上,我们看到列表是另一种类型的多态类型约束(此处为[])。然后当我们谈到list monad的bind运算符时,我们谈论[] a -> (a -> [] b) -> [] b定义的bind运算符。

你想要实现的是bind运算符定义为[] Maybe a -> (a -> [] b) -> [] b,这不是第一个的特殊版本,而是另一个函数,关于它的类型签名我真的怀疑它可以是{任何类型的monad的{1}}运算符,因为你没有返回你消耗的东西。你肯定会使用一个函数从一个monad转到另一个monad但是这个函数肯定不是list的bind运算符的另一个版本。

这就是为什么我说,对于特定的数据类型没有意义,据我所知,你只能有bind的一个定义。

答案 3 :(得分:1)

flatMap(>>=)并未对您的Option[Try[ ]]示例进行类型检查。在伪Haskell表示法中

type OptionTry x = Option (Try x)

instance Monad OptionTry where
  (>>=) :: OptionTry a -> (a -> OptionTry b) -> OptionTry b
  ...

我们需要bind / flatMap返回包含在与输入值相同的上下文中的值。

我们也可以通过查看Monad的等效return / join实现来看到这一点。对于OptionTryjoin具有专门类型

instance Monad OptionTry where
  join :: OptionTry (OptionTry a) -> OptionTry a
  ...

有点眯着眼睛,flatMap的“扁平”部分应该是join(或concat,对于名称来源的列表,应该很清楚。)

现在,单个数据类型可能有多个不同的bind。从数学上讲,Monad实际上是数据类型(或者,实际上是monad所包含的值集)以及特定的bindreturn操作。不同的操作导致不同的(数学)Monads。