Haskell 为什么它是绑定的';s参数';单位价值的责任是什么?
典型的monadHaskell 为什么它是绑定的';s参数';单位价值的责任是什么?,haskell,functional-programming,monads,Haskell,Functional Programming,Monads,典型的monadbind函数具有以下签名: m a -> (a -> m b) -> m b 据我所知(我可能是错的),函数(a->mb)只是一个从一个结构a到另一个结构b的映射函数。假设这是正确的,那么问题就来了,为什么bind的签名不仅仅是: m a -> (a -> b) -> m b 假设单位是单子定义的一部分;为什么要给出函数(a->mb)调用unit它产生的任何值b的责任——让它成为bind的一部分不是更明智吗?一个A->mb类型的函数可能比一
bind
函数具有以下签名:
m a -> (a -> m b) -> m b
据我所知(我可能是错的),函数(a->mb)
只是一个从一个结构a
到另一个结构b
的映射函数。假设这是正确的,那么问题就来了,为什么bind
的签名不仅仅是:
m a -> (a -> b) -> m b
假设
单位
是单子定义的一部分;为什么要给出函数(a->mb)
调用unit
它产生的任何值b
的责任——让它成为bind
的一部分不是更明智吗?一个A->mb
类型的函数可能比一个A->b
然后是return
(或者你称之为“unit”)类型的函数有更多的功能)事实上,没有“有效的”操作可以用后一种形式表示。像ma->(A->b)->mb
这样的函数将等同于fmap::(A->b)->fa->fb
。fmap
所能做的就是更改动作中的值,它不能执行新动作。使用ma->(a->mb)->mb
,您可以“运行”ma
,将该值输入(a->mb)
,然后返回mb
的新效果。如果没有这一点,你将永远只能在你的程序中有一个效果,你不能有两个连续的打印语句,你不能连接到网络然后下载一个URL,你不能响应用户输入,你将只能转换从每个基本操作返回的值。正是这种操作使得单子比函子或应用程序更强大
这里的另一个细节是,您不一定只是用unit
包装一个值,mb
可以表示一个动作,而不仅仅是返回一些东西。例如,在操作putStrLn::String->m()
中对return
的调用在哪里?此函数的签名与>=
的第二个参数兼容,与a~String
和b~()
兼容,但在函数体的任何地方都没有对return
的调用。>=
的要点是将两个动作排序在一起,而不仅仅是在一个上下文中包装值。因为ma->(a->b)->mb
就是Monas拥有的fmap
,作为函子。
单子添加到函子中的是将嵌套单子连接(或挤压)到简单单子的能力。
示例一个列表到简单列表的列表,或[[1,2],[3]]
到[1,2,3]
如果在fmap
签名中,将b
替换为mb
,则会得到
m a -> (a -> m b) -> m (m b)
使用普通函子,您将被困在双层容器中(m(mb)
)。用单子,
使用join
功能,可以将m(mb)
挤压到mb
。所以bind
实际上是join.fmap
事实上,join
和fmap
只能使用bind
(和return
)编写,因此在实践中,只定义一个函数bind
,而不是两个join
和fmap
,尽管后者的编写通常更简单
因此,基本上,bind
是fmap
和join
的混合体,这是另一种说法:任何有用的monad都有许多特定于它的操作,而不仅仅是来自monadic接口的操作。例如,IO
monad有getLine::IO字符串
。考虑这个非常简单的程序:
main :: IO ()
main = do name <- prompt "What's your name?"
putStrLn ("Hello " ++ name ++ "!")
prompt :: String -> IO String
prompt str = do putStrLn str
getLine
main::IO()
main=do name IO字符串
prompt str=do putStrLn str
getLine
请注意,prompt
的类型适合a->mb
模具,但它不在任何地方使用return
(也称单元
)。这是因为它使用了getLine::IO String
,这是一个由IO
monad提供的不透明操作,并且不能根据返回和=
来定义
这样想:归根结底,Monad
永远不是你自己使用的东西;它是一个接口,用于将外部事物(如getLine
和putStrLn
)连接在一起
据我所知(我可能是错的),函数(a->mb)
只是从一个结构a
到另一个结构b
的映射函数
你说得很对——如果你把“映射”这个词改为态射。对于函数a->mb
,它们是monad的。有鉴于此,monad的特征在于,您可以用与编写函数相同的方式编写kleilis:
type Kleisli m a b = a -> m b -- `Control.Arrow` has this as a `newtype` with `Category` instance.
-- compare (.) :: (b->c) -> (a->b) -> a->c
(<=<) :: Kleisli m b c -> Kleisli m a b -> Kleisli m a c
(f<=<g) x = f =<< g x
然而,Kleislis严格来说比函数更强大。例如,对于m≡ IO
,它们基本上是会产生副作用的函数,正如您所知,普通Haskell函数不会产生副作用。所以你不能把一个Kleisli变回一个函数——如果>=
接受一个a->b
而不是Kleisli m a b
,但你所拥有的只是一个Kleisli,就没有办法使用它了。因为对于许多具体的单子来说,定义不仅仅是单元,相反:bind
允许您中间删除一层m
s,因为它相当于flip$\f->join。fmap f
,其中join::Monad m=>m(ma)->ma
。Monad不仅仅是关于动作、效果和IO。@Bergi是正确的,但是使用效果和IO的示例进行解释通常很简单,特别是考虑到大量的Monad要么堆叠在IO的顶部,要么在某个点与IO绑定。使用它们更直观。因为OP的问题的答案
(return.) :: (a->b) -> Kleisli m a b