6

flatMap在 Monad 中定义多个(或>>=bindHaskell 中)方法是否有意义?我实际使用的极少数 monad ( Option, Try, Eitherprojections) 只定义了一个 flatMap 方法。

flatMap例如,定义一个方法是否有意义,该方法Option将在其上生成一个函数Try?例如,这Option[Try[User]]会被展平Option[User]吗?(考虑到丢失异常不是问题......)

或者一个 monad 应该只定义一个flatMap方法,采用一个产生相同类型 monad 的函数?我想在这种情况下,Either预测不会是单子?他们是吗?

4

4 回答 4

5

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

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]
于 2013-05-06T22:25:42.247 回答
1

这取决于“有意义”是什么意思。

如果您的意思是它是否符合单子定律,那么我并不完全清楚这个问题是否完全有道理。我必须看到一个具体的建议才能告诉我。如果您按照我认为您建议的方式进行操作,那么至少在某些极端情况下,您可能最终会违反构图。

如果你的意思是它有用,当然,你总能找到这些东西有用的案例。问题是,如果你开始违反单子定律,你就会在代码中为粗心的函数式(范畴论)推理器留下陷阱。最好让看起来有点像 monads 的东西实际上是 monads(一次只有一个,尽管你可以提供一种明确的方式来切换 la Either——但你是对的,严格来说,不是LeftProjectionmonads RightProjection)。或者写出非常清晰的文档来解释它不是它看起来的样子。否则,假设法律成立,有人会欣然接受,并且* splat *。

于 2013-05-06T21:31:12.820 回答
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

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

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

一个用例来说明,

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

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

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

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

于 2013-05-06T21:55:13.363 回答
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实现来看到这一点。对于OptionTry,join有专门的类型

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

稍微眯眼应该清楚的是,“扁平”部分flatMapjoin(或concat名称源自的列表)。

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

于 2013-05-06T22:11:29.150 回答