为什么haskell的绑定函数从非一元到一元是函数 [英] Why does haskell's bind function take a function from non-monadic to monadic

查看:86
本文介绍了为什么haskell的绑定函数从非一元到一元是函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Haskell中绑定函数(>> =)的定义存在一些疑问。



<因为Haskell是一种纯语言,所以我们可以使用Monad来处理带有副作用的操作。我认为这种策略有点像把所有的行为都可能给另一个世界带来副作用,我们可以从我们的纯粹的哈斯克尔世界控制它们,尽管 do 或<$ c $因此当我看到>> = 的定义时, code> function

 (>> =):: Monad m => m a  - > (a  - > m b) - > mb 

需要一个(a - > mb)函数,因此前一个操作的结果 ma 可以解包为一个非单值 a in >> = 。然后函数(a - > mb) a 作为输入并返回另一个一元 mb 作为结果。

我的问题是为什么我们使用一个(a - > mb)功能?在我看来, m a - > m b 函数也可以做到这一点。是否有任何理由,或仅仅因为它是这样设计的?

编辑

从评论中我明白很难从 ma 中提取 a 。不过,我认为我可以将一元一元 ma 看作一个带有副作用的 a



是否可以假设函数 ma - > m b 的作用类似于 a - > b ,所以我们可以定义 m a - > m b 就像定义 a - > b

解决方案

edit2: ve从一开始就说:

Monads是EDSLs,



E 嵌入域特定语言。 嵌入意味着语言的语句在我们的语言中是简单的值,Haskell。



让我们试着让我们 IO -language。假设我们有 print1 :: IO()原语,它描述了在提示符下打印整数 1 的动作。想象一下,我们也有 print2 :: IO()。两者都是普通的Haskell值。在Haskell中,我们讲述了这些行为。这个 IO 语言仍然需要在运行时系统的某些部分之后在运行时解释/执行。有两种语言,我们有两个世界,两个时间轴。



我们可以写 do {print1; print2} 来描述复合动作。但是我们不能在提示符下打印 3 ,因为它是纯粹的 外部 Haskell世界。我们这里有一个EDSL,但显然不是一个非常强大的。我们在这里必须有无限的原始供应;不是一个成功的命题。它甚至不是Functor,因为我们无法修改这些值。



现在,如果我们能够做到?然后我们可以知道 do {print1; print2; fmap(1+)print2} ,以便打印出 3 。现在它是一个Functor。

我们为构造这些动作描述符提供了灵活性(例如 print1 )。它是例如 print :: Show a => a - > IO a 。我们现在可以讨论更多功能的操作,如 do {print 42; getLine; putStrLn(Hello,++... you!)}



但是现在我们看到需要引用之前操作的results。我们希望能够编写 do {print 42; s< - getLine; putStrLn(Hello,++ s ++!)} 。我们希望基于之前的 IO 动作的结果(在Haskell世界中)创建(在Haskell世界中)新的动作描述(描述 IO -world中的动作的Haskell值)这些 IO -actions 产生,运行时 语言被解释(它描述的操作在 IO -world中进行)。



这意味着可以创建这些 IO - 来自Haskell值的语言语句,如 print :: a - > IO a 。这正是你所问的类型,而是什么使这个EDSL成为Monad






想象一下,我们有一个IO原语( a_primitive :: IO Int - > IO()),它将任何正整数是,然后在打印任何非正整数之前在单独一行中打印---。然后我们可以按照你的建议写 a_primitive(return 1)

但IO已关闭;它是不纯的;我们不能在Haskell中编写新的IO原语,并且不可能有任何可能进入我们脑海的新想法定义的原语。因此,我们编写(\ x - >如果x> 0,则打印x else do {putStrln---; print x}),而lambda表达式的类型是 Int - > IO()(或多或少)。

如果上述lambda表达式中的参数 x 类型为 IO Int ,表达式 x> 0 将错误输入,并且没有办法从 IO a a >不需要使用标准>> = 运算符。



另请参阅:








编辑:这四种类型的应用程序:

 ($)::(a  - > b) - > a  - > b 
(< $>):: Functor f => (a - > b) - > f a - > f b
(*)::适用的f => f(a - > b) - > f a - > f b
(=<<):: Monad f => (a - > f b) - > f a - > f b

为什么?他们只是。你的问题是真的,为什么我们需要Monads?为什么要使用函数或应用函子?这肯定已经被多次询问和回答(例如,上面列表中的第二个链接)。首先,正如我试图在上面展示的那样,monads让我们在Haskell中编写新的计算


I have some questions about the definition of the binding function (>>=) in Haskell.

Because Haskell is a pure language, so we can use Monad to handle operations with side effects. I think this strategy is somewhat like putting all actions may cause side effects to another world, and we can control them from our "pure" haskell world though do or >>=.

So when I look at definition of >>= function

(>>=) :: Monad m => m a -> (a -> m b) -> m b

it takes a (a -> m b) function, so result m a of the former action can be "unpack" to a non-monadic a in >>=. Then the function (a -> m b) takes a as its input and return another monadic m b as its result. By the binding function I can operate on monadic without bringing any side effects into pure haskell codes.

My question is why we use a (a -> m b) function? In my opinion, a m a -> m b function can also do this. Is there any reason, or just because it is designed like this?

EDIT

From comments I understand it's hard to extract a from m a. However, I think I can consider a monadic m a as a a with side effect.

Is it possible to assume function m a -> m b acts similar with a -> b, so we can define m a -> m b like defining a -> b?

解决方案

edit2: OK, here's what I should've said from the start:

Monads are EDSLs,

E as in embedded domain-specific languages. Embedded means that the language's statements are plain values in our language, Haskell.

Let's try to have us an IO-language. Imagine we have print1 :: IO () primitive, describing an action of printing an integer 1 at the prompt. Imagine we also have print2 :: IO (). Both are plain Haskell values. In Haskell, we speak of these actions. This IO-language still needs to be interpreted / acted upon by some part of the run-time system later, at "run"-time. Having two languages, we have two worlds, two timelines.

We could write do { print1 ; print2 } to describe compound actions. But we can't create a new primitive for printing 3 at the prompt, as it is outside our pure Haskell world. What we have here is an EDSL, but evidently not a very powerful one. We must have an infinite supply of primitives here; not a winning proposition. And it is not even a Functor, as we can't modify these values.

Now, what if we could? We'd then be able to tell do { print1 ; print2 ; fmap (1+) print2 }, to print out 3 as well. Now it's a Functor. More powerful, still not flexible enough.

We get flexibility with primitives for constructing these action descriptors (like print1). It is e.g. print :: Show a => a -> IO a. We can now talk about more versatile actions, like do { print 42; getLine ; putStrLn ("Hello, " ++ "... you!") }.

But now we see the need to refer to the "results" of previous actions. We want to be able to write do { print 42; s <- getLine ; putStrLn ("Hello, " ++ s ++ "!") }. We want to create (in Haskell world) new action descriptions (Haskell values describing actions in IO-world) based on results (in Haskell world) of previous IO-actions that these IO-actions will produce, when they are run, when the IO-language is interpreted (the actions it describes been carried out in IO-world).

This means the ability to create those IO-language statements from Haskell values, like with print :: a -> IO a. And that is exactly the type you're asking about, and it is what makes this EDSL a Monad.


Imagine we have an IO primitive (a_primitive :: IO Int -> IO ()) which prints any positive integer as is, and prints "---" on a separate line before printing any non-positive integer. Then we could write a_primitive (return 1), as you suggest.

But IO is closed; it is impure; we can't write new IO primitives in Haskell, and there can't be a primitive already defined for any new idea that may come into our heads. So we write (\x -> if x > 0 then print x else do { putStrln "---"; print x }) instead, and that lambda expression's type is Int -> IO () (more or less).

If the argument x in the above lambda-expression were of type IO Int, the expression x > 0 would be mistyped, and there is no way to get that a out of IO a directly, without the use of the standard >>= operator.

see also:


edit: These are four types of application:

($)   ::                     (a ->   b) ->   a ->   b
(<$>) :: Functor f     =>    (a ->   b) -> f a -> f b
(<*>) :: Applicative f =>  f (a ->   b) -> f a -> f b
(=<<) :: Monad f       =>    (a -> f b) -> f a -> f b

Why? They just are. Your question is really, why do we need Monads? Why Functors or Applicative Functors aren't enough? And this was surely already asked and answered many times (e.g., the 2nd link in the list just above). For one, as I tried to show above, monads let us code new computations in Haskell.

这篇关于为什么haskell的绑定函数从非一元到一元是函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆