为什么一个函数不能使用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 -> b
, fmap f :: ma -> mb
基本上就是这样。 但是, fmap
本身并不能让你“查看” Monad m => ma
但无法从中逃脱。 然而, join
和fmap
一起给你这种能力。 (>>=)
可以用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 => ma
, Monad m => a -> mb
, Monad 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
获得的结果传递给它。 如果你有fmap
和join
而不是>>=
你必须这样写:
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 = a
, y = mb
和z = 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 -> mb
“ fmap 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?