Haskell 为什么它是绑定的';s参数';单位价值的责任是什么?

Haskell 为什么它是绑定的';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类型的函数可能比一

典型的monad
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