为什么一个函数不能使用monadic值并返回另一个monadic值?

假设我们有两个monadic函数:

  f :: a -> m b
  g :: b -> m c
  h :: a -> m c

绑定函数被定义为

(>>=) :: m a -> (a -> m b) -> m b

我的问题是为什么我们不能做下面的事情。 声明一个函数,它将采用一个monadic值并返回另一个monadic值?

  f :: a -> m b
  g :: m b -> m c
  h :: a -> m c

绑定函数被定义为

(>>=) :: m a -> (ma -> m b) -> m b

haskell中限制函数将单数值作为参数的内容是什么?

编辑:我想我没有明确表示我的问题。 关键是,当你使用绑定运算符来编写函数时,为什么绑定运算符的第二个参数是一个非单值的函数( b )? 为什么它不能取一个单数值( mb )并返回mc 。 是不是,当你正在处理单子时,你构成的功能将总是具有以下类型。

  f :: a -> m b
  g :: b -> m c
  h :: a -> m c

h = f 'compose' g

我正在尝试学习单子,这是我无法理解的。


Monad一个关键能力是“向内看” ma类型并看到a ; 但Monad一个关键限制是monad必须是“不可避免的”,也就是说, Monad类型类操作不应足以编写Monad m => ma -> a类型的函数。 (>>=) :: Monad m => ma -> (a -> mb) -> mb正是给了你这个能力。

但是有多种方法可以实现这一目标。 Monad类可以像这样定义:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Functor f => Monad m where
    return :: a -> m a
    join :: m (m a) -> m a

你问为什么我们可能没有Monad m => ma -> (ma -> mb) -> mb函数。 那么,给定f :: a -> bfmap f :: ma -> mb基本上就是这样。 但是, fmap本身并不能让你“查看” Monad m => ma但无法从中逃脱。 然而, joinfmap一起给你这种能力。 (>>=)可以用fmap join ,并join

(>>=) :: Monad m => m a -> (a -> m b) -> m b
ma >>= f = join (fmap f ma)

事实上,这是一个定义Monad实例的常用技巧,当你无法提出(>>=)的定义时,为你想要的monad写入join函数,然后使用通用定义(>>=)


那么,这就回答了“它是否应该是它的样子”,是否定的问题的一部分。 但是,为什么它是这样呢?

我不能为Haskell的设计师说话,但我喜欢这样想:在Haskell一元编程中,基本构建块就是这样的行为:

getLine :: IO String
putStrLn :: String -> IO ()

更一般地说,这些基本构建块的类型看起来像Monad m => maMonad m => a -> mbMonad m => a -> b -> mc ,..., Monad m => a -> b -> ... -> mz 。 人们非正式地称这些行为。 Monad m => ma是无参数动作, Monad m => a -> mb是单参数动作,依此类推。

那么, (>>=) :: Monad m => ma -> (a -> mb) -> mb基本上是“连接”两个动作的最简单的函数。 getLine >>= putStrLn是首先执行getLine的动作,然后执行putStrLn将从执行getLine获得的结果传递给它。 如果你有fmapjoin而不是>>=你必须这样写:

join (fmap putStrLn getLine)

更一般地说, (>>=)体现了一个概念,就像一个“管道”动作一样,因此它是使用monad作为一种编程语言的更有用的操作符。


最后的事情:确保你了解Control.Monad模块。 虽然return(>>=)是monads的基本函数,但您可以使用这两个函数定义其他更多的高级函数,并且该模块会收集几十个更常见的函数。 你的代码不应该被(>>=)强迫进入紧箍咒; 这是一个至关重要的构建模块,既可以单独使用,也可以作为较大构建模块的组件。


为什么我们不能做下面的事情。 声明一个函数,它将采用一个monadic值并返回另一个monadic值?

f :: a -> m b
g :: m b -> m c
h :: a -> m c

我是否明白你想写下以下内容?

compose :: (a -> m b) -> (m b -> m c) -> (a -> m c)
compose f g = h where
  h = ???

事实证明,这只是规则的函数组合,但参数的顺序却相反

(.) :: (y -> z) -> (x -> y) -> (x -> z)
(g . f) = x -> g (f x)

我们选择使用类型x = ay = mbz = mc来专门化(.)

(.) :: (m b -> m c) -> (a -> m b) -> (a -> m c)

现在翻转输入的顺序,并获得所需的compose功能

compose :: (a -> m b) -> (m b -> m c) -> (a -> m c)
compose = flip (.)

请注意,我们在这里的任何地方都没有提到monad。 这适用于任何类型的构造函数m ,无论它是否是monad。


现在让我们考虑一下你的其他问题。 假设我们想写下如下内容:

composeM :: (a -> m b) -> (b -> m c) -> (a -> m c)

停止。 Hoogle时间。 为这种类型的签名ogogling,我们发现有一个完全匹配! 它是来自Control.Monad的>=> ,但请注意,对于此函数, m必须是monad。

现在的问题是为什么。 是什么让这个作品与另一个作​​品有所不同,以至于这个作品需要m是Monad,而另一个则不是? 那么,这个问题的答案就在于理解Monad抽象的全部内容,所以我会留下更详细的答案来讨论关于这个主题的各种互联网资源。 只要说如果不知道m话就没有办法写composeM 。 来吧,试试吧。 如果没有关于m是什么的额外知识,你就不能编写它,并且编写这个函数所需的附加知识正好是m具有Monad的结构。


让我稍微解释一下你的问题:

为什么我们不能使用Monad的g :: ma -> mb类型的函数?

答案是, 我们已经与Functors合作 。 没有什么特别的关于fmap f :: Functor m => ma -> mbfmap f :: Functor m => ma -> mb ”,其中f :: a -> b 。 Monads是Functor; 我们通过使用良好的旧fmap来获得这样的功能:

class Functor f a where
    fmap :: (a -> b) -> f a -> f b
链接地址: http://www.djcxy.com/p/43359.html

上一篇: why can't a function take monadic value and return another monadic value?

下一篇: Combining monads in Haskell