pol*_*pts 7 monads haskell functional-programming
假设我们有两个monadic函数:
f :: a -> m b
g :: b -> m c
h :: a -> m c
Run Code Online (Sandbox Code Playgroud)
绑定函数定义为
(>>=) :: m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)
我的问题是为什么我们不能做类似下面的事情.声明一个函数,该函数将采用monadic值并返回另一个monadic值?
f :: a -> m b
g :: m b -> m c
h :: a -> m c
Run Code Online (Sandbox Code Playgroud)
绑定函数定义为
(>>=) :: m a -> (ma -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)
haskell中有什么限制函数采用monadic值作为其参数?
编辑:我想我没有说清楚我的问题.关键是,当你使用bind运算符编写函数时,为什么bind运算符的第二个参数是一个采用非monadic值(b
)的函数?为什么不能采取monadic值(mb
)并回馈mc
.是这样的,当你处理monad并且你将要编写的函数将始终具有以下类型.
f :: a -> m b
g :: b -> m c
h :: a -> m c
Run Code Online (Sandbox Code Playgroud)
和 h = f 'compose' g
我正在努力学习单子,这是我无法理解的.
一个关键的能力Monad
是"向内看" m a
类型并看到一个a
; 但一个关键的限制Monad
是monad必须是"不可避免的",即Monad
类型类操作不应该足以编写类型的函数Monad m => m a -> a
. (>>=) :: Monad m => m a -> (a -> m b) -> m b
给你这个能力.
但实现这一目标的方法不止一种.本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
Run Code Online (Sandbox Code Playgroud)
你问为什么我们没有Monad m => m a -> (m a -> m b) -> m b
功能.嗯,给定f :: a -> b
,fmap f :: ma -> mb
基本上就是这样.但是fmap
,它本身并不能让你"向内看" Monad m => m a
但却无法摆脱它.然而join
,fmap
一起给你这种能力. (>>=)
可一般被写入与fmap
和join
:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
ma >>= f = join (fmap f ma)
Run Code Online (Sandbox Code Playgroud)
实际上,这是一个常见的技巧,用于定义一个Monad
实例,当你遇到定义(>>=)
-write join
函数为你想成为monad时,然后使用泛型定义(>>=)
.
嗯,这回答了"它必须是它的方式"问题的一部分与"不".但是,它为什么会这样呢?
我不能代表Haskell的设计者,但我喜欢这样思考:在Haskell monadic编程中,基本构建块是这样的动作:
getLine :: IO String
putStrLn :: String -> IO ()
Run Code Online (Sandbox Code Playgroud)
更一般地,这些基本构造块看起来像类型Monad m => m a
,Monad m => a -> m b
,Monad m => a -> b -> m c
,..., Monad m => a -> b -> ... -> m z
.人们非正式地称这些行为. Monad m => m a
是一个无争论的行为,Monad m => a -> m b
是一个单一的行为,等等.
好吧,(>>=) :: Monad m => m a -> (a -> m b) -> m b
基本上是"连接"两个动作的最简单的功能. getLine >>= putStrLn
是首先执行的操作,getLine
然后执行putStrLn
传递从执行中获得的结果getLine
.如果你有fmap
,join
而不是>>=
你必须写这个:
join (fmap putStrLn getLine)
Run Code Online (Sandbox Code Playgroud)
更一般地说,(>>=)
体现了一个很像行为"管道"的概念,因此使用monads作为一种编程语言更有用.
最后一件事:确保您了解该Control.Monad
模块.虽然return
并且(>>=)
是monad的基本功能,但是你可以使用这两个函数来定义其他更多高级函数,并且该模块会收集几十个更常见的函数.你的代码不应该被迫穿上紧身衣(>>=)
; 它是一个关键的构建块,既可以单独使用,也可以作为较大构建块的组件.
为什么我们不能做类似下面的事情.声明一个函数,该函数将采用monadic值并返回另一个monadic值?
f :: a -> m b
g :: m b -> m c
h :: a -> m c
Run Code Online (Sandbox Code Playgroud)
我明白你想写下面的内容吗?
compose :: (a -> m b) -> (m b -> m c) -> (a -> m c)
compose f g = h where
h = ???
Run Code Online (Sandbox Code Playgroud)
事实证明,这只是常规的函数组合,但参数顺序相反
(.) :: (y -> z) -> (x -> y) -> (x -> z)
(g . f) = \x -> g (f x)
Run Code Online (Sandbox Code Playgroud)
让我们选择专注(.)
与类型x = a
,y = m b
以及z = m c
(.) :: (m b -> m c) -> (a -> m b) -> (a -> m c)
Run Code Online (Sandbox Code Playgroud)
现在翻转输入的顺序,您就可以获得所需的compose
功能
compose :: (a -> m b) -> (m b -> m c) -> (a -> m c)
compose = flip (.)
Run Code Online (Sandbox Code Playgroud)
请注意,我们在这里任何地方都没有提到monad.这适用于任何类型的构造函数m
,无论它是否为monad.
现在让我们考虑你的另一个问题.假设我们要写以下内容:
composeM :: (a -> m b) -> (b -> m c) -> (a -> m c)
Run Code Online (Sandbox Code Playgroud)
停止.霍格时间.对于那种类型的签名,我们发现有完全匹配!它>=>
来自Control.Monad,但请注意,对于此函数,m
必须是monad.
现在问题是为什么.是什么让这个来自组成不同的其它一个,使得这一个要求m
是一个单子,而其他不?那么,这个问题的答案在于理解Monad抽象是什么的核心,所以我将对谈论这个主题的各种互联网资源留下更详细的答案.我只想说,没有办法写composeM
不知道的东西有关m
.来吧,试一试.如果没有关于什么m
的额外知识,你就无法编写它,而编写这个函数所需的额外知识恰好就是m
具有a的结构Monad
.