bind可以由fmap和join组成,所以我们必须使用monadic函数a-> m b? [英] bind can be composed of fmap and join, so do we have to use monadic functions a -> m b?
问题描述
我不常使用Haskell,但我了解Monads的概念.
Kleisli三元组使我感到困惑,但是类别>
尽管Haskell根据return和bind函数定义了monad,但也可能根据
return
和另外两个操作join
和fmap
定义了monad.这种表述更符合范畴理论中单子的原始定义.类型为(t → u) → M t → M u
的fmap
操作采用两种类型之间的函数,并产生对monad中的值执行相同操作"的函数.join
操作(类型为M (M t) → M t
)将两层单子信息平化"为一层.
帮助我了解Monads的背景原理.
这两个表述如下:
fmap f m = m >>= (return . f)
join n = n >>= id
fmap :: (a -> b) -> (m a -> m b)
unit :: a -> m a
join :: m (m a) -> m a
>>= :: m a -> (a -> m b) -> m b
m >>= f = join $ fmap f m
我的问题是:
我认为,由于>>=
可以由fmap
和join
组成,所以它的单子函数
a -> m b
不是必需的,普通函数a -> b
可以满足该操作,但是网络上的许多教程仍然坚持使用monadic函数,因为这是Kleisli三元组和monad-laws.
为简单起见,我们是否不应该仅使用非一元函数(只要它们是内函数)?我想念什么?
相关主题是
从某种意义上说,你是对的.由于每个monad m
都是函子,我们可以将fmap f
与函数f :: a -> b
结合使用,将m a
转换为m b
,但是有一个陷阱.什么是b
?
我喜欢将m
理解为计划获得"的意思,其中计划"涉及除纯计算之外的某种其他交互作用.如果您有计划获得Int
"并且想要计划获得String
",则可以将fmap
与Int -> String
中的函数一起使用,但是该函数的类型告诉您您从Int
获取String
不需要进一步的交互.
并非总是如此:也许Int
是学生注册号,而String
是他们的名字,所以从一个转换为另一个的计划需要在某些表中进行外部查找.然后,我没有从Int
到String
的纯函数,而是从Int
到计划获取String
"的纯函数.如果我在我的计划获得Int
"中fmap
表示满意,但是我最终得到了计划获得(plan-to-get String
)",我需要
一般情况是,我们有足够的信息来计算计划以获得更多.这就是a -> m b
的模型.尤其是,我们有return :: a -> m a
,它使我们所拥有的信息变成了计划,而无需采取进一步的行动就可以为我们提供准确的信息,而我们有(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
,它构成了两件事.我们还认为(>=>)
是关联的并且在左右吸收return
,这与;
是关联的并且吸收skip
在经典命令式编程中一样.
使用这种组合方法从较小的计划中构建较大的计划更为方便,同时将计划获得"层的数量保持一致.否则,您需要使用fmap
构建一个 n 层计划,然后在外部执行正确数量的join
(这将是该计划的易碎属性). /p>
现在,由于Haskell是一种具有自由变量"和范围"概念的语言,因此
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
代表总体输入信息"只是从我们已经拥有的事物的范围出发而已,
(>>=) :: m b -> (b -> m c) -> m c
然后我们得到"bind",它是一种以程序员最友好的形式呈现组成结构的工具,类似于本地定义.
总而言之,您可以使用a -> b
,但是通常您需要b
成为获得计划的东西",这对于选择组合构建计划是有帮助的选择.
I don't use Haskell a lot, but I understand the concept of Monads.
I had been confused by Kleisli triple, and the category, however,
Although Haskell defines monads in terms of the return and bind functions, it is also possible to define a monad in terms of
return
and two other operations,join
andfmap
. This formulation fits more closely with the original definition of monads in category theory. Thefmap
operation, with type(t → u) → M t → M u
, takes a function between two types and produces a function that does the "same thing" to values in the monad. Thejoin
operation, with typeM (M t) → M t
, "flattens" two layers of monadic information into one.
helps me to the background principle of Monads.
The two formulations are related as follows:
fmap f m = m >>= (return . f)
join n = n >>= id
fmap :: (a -> b) -> (m a -> m b)
unit :: a -> m a
join :: m (m a) -> m a
>>= :: m a -> (a -> m b) -> m b
m >>= f = join $ fmap f m
My question is:
I think that since >>=
can be composed of fmap
and join
, a monadic function
a -> m b
is not required and normal functions a -> b
will satisfy the operation, but so many tutorials around the web still insist to use a monadic functions since that is the Kleisli triple and the monad-laws.
Well, shouldn't we just use non-monadic functions, as long as they are endo-functions, for the simplicity? What do I miss?
Related topics are
Haskell Monad bind operator confusion
Difference in capability between fmap and bind?
In a sense, you're right. As every monad m
is a functor, we can use fmap f
with a function f :: a -> b
to turn an m a
into an m b
, but there's a catch. What's b
?
I like to think of such an m
as meaning "plan-to-get", where "plans" involve some sort of additional interaction beyond pure computation. If you have a "plan-to-get Int
" and you want a "plan-to-get String
", you can use fmap
with a function in Int -> String
, but the type of that function tells you that getting the String
from the Int
involves no further interaction.
That isn't always so: perhaps the Int
is a student registration number and the String
is their name, so the plan to convert from one to the other needs an external lookup in some table. Then I don't have a pure function from Int
to String
, but rather a pure function from Int
to "plan-to-get String
". If I fmap
that across my "plan-to-get Int
", that's fine, but I end up with "plan-to-get (plan-to-get String
)" and I need to join
the outer and inner plans.
The general situation is that we have enough information to compute the plan to get more. That's what a -> m b
models. In particular, we have return :: a -> m a
, which turns the information we have into the plan that gives us exactly that information by taking no further action, and we have (>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
which composes two such things. We also have that (>=>)
is associative and absorbs return
on left and right, much the way ;
is associative and absorbs skip
in classic imperative programming.
It's more convenient to build larger plans from smaller ones using this compositional approach, keeping the number of "plan-to-get" layers a consistent one. Otherwise, you need to build up an n-layer plan with fmap
, then do the right number of join
s on the outside (which will be a brittle property of the plan).
Now, as Haskell is a language with a concept of "free variable" and "scope", the a
in
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
representing the "overall input information" can just be taken to come from the scope of things we already have, leaving
(>>=) :: m b -> (b -> m c) -> m c
and we get back "bind", which is the tool that presents the compositional structure in the most programmer-friendly form, resembling a local definition.
To sum up, you can work with a -> b
, but often you need b
to be "plan-to-get something", and that's the helpful thing to choose if you want to build plans compositionally.
这篇关于bind可以由fmap和join组成,所以我们必须使用monadic函数a-> m b?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!