简体   繁体   English

Haskell`回文=反向>>=(==)`

[英]Haskell `palindrome = reverse >>= (==)`

Can someone explain how this function for palindrome in Haskell works:有人可以解释一下 function 中 Haskell 的回文是如何工作的:

palindrome :: Eq a => [a] -> Bool
palindrome = reverse >>= (==)

-- type declarations
reverse :: [a] -> [a]
>>= :: Monad m => m a -> (a -> m b) -> m b
(reverse >>=) :: ([a] -> [a] -> b) -> [a] -> b
(==) :: Eq a => a -> a -> Bool

In particular, how does the definition of the Monad typeclass on functions work, and how does it somehow reduce the number of inputs for (==) from two to one list?特别是,函数上的 Monad 类型类的定义如何工作,以及它如何以某种方式将 (==) 的输入数量从两个列表减少到一个列表?

This is a bit mind-blowing, so strap in:-)这有点令人兴奋,所以请注意:-)

A monad is a type constructor that takes one type parameter. monad 是一个类型构造函数,它接受一个类型参数。 For example, Maybe is such a type constructor.例如, Maybe就是这样一个类型构造函数。 So, Maybe is a monad, and then Maybe Int is a monadic value in that monad.所以, Maybe是一个 monad,然后Maybe Int是那个 monad 中的一个 monadic 值。 Similarly, IO is a monad, and then IO Int is a monadic value in that monad.同样, IO是一个单子,然后IO Int是该单子中的单子值。

Now, it is also possible to make a monad out of a type constructor that has two type parameters.现在,也可以从具有两个类型参数的类型构造函数中创建一个 monad。 To do that, we just need to fix the first one.为此,我们只需要修复第一个。 For example, look at Either : it has two parameters, but a monad must have only one.例如,查看Either :它有两个参数,但 monad 必须只有一个。 So we fix the first parameter.所以我们固定第一个参数。 Thus Either Bool is a monad, and then Either Bool Int is a monadic value in that monad.因此, Either Bool是一个 monad,然后Either Bool Int是该 monad 中的一个 monadic 值。 Similarly, Either String is a completely different monad, and then Either String Int is a monadic value in that monad.类似地, Either String是一个完全不同的 monad,然后Either String Int是该 monad 中的一个 monadic 值。

On the other hand, look at how functions are denoted:另一方面,看看函数是如何表示的:

a -> b

But this is just infix-operator trickery, similar to 5 + 42 or "foo" <> "bar" .但这只是中缀运算符的诡计,类似于5 + 42"foo" <> "bar" This infix notation can be "canonicalized" like this:这个中缀符号可以像这样“规范化”:

(->) a b

So really, "function" can be seen as a type constructor that has two type parameters, just like Either does.所以真的,“函数”可以看作是一个有两个类型参数的类型构造函数,就像Either一样。 For the "function" constructor, these parameters have a specific meaning - one is "function input" and the other is "function output".对于“函数”构造函数来说,这些参数具有特定的含义——一个是“函数输入”,另一个是“函数输出”。

Ok, now we're ready to look at functions as monads.好的,现在我们已经准备好将函数视为 monad。 Just like with Either , in order to do this, we have to fix the first type parameter.就像Either一样,为了做到这一点,我们必须修复第一个类型参数。 Thus, for example, (->) Bool is a monad, and then (->) Bool Int (also known as Bool -> Int ) is a monadic value in that monad.因此,例如, (->) Bool是一个单子,然后(->) Bool Int (也称为Bool -> Int )是该单子中的单子值。 Similarly, (->) String is a completely different monad, and then (->) String Int (also known as String -> Int ) is a monadic value in that monad.类似地, (->) String是一个完全不同的 monad,然后(->) String Int (也称为String -> Int )是该 monad 中的一元值。

To give you an intuition, one way of looking at it is that a monadic value in such monad means a "promise" - that is, " you give me a String and I give you an Int back ".给你一个直觉,看待它的一种方式是,这种 monad 中的 monadic 值意味着“承诺”——即“你给我一个String ,我给你一个Int返回”。 And then the standard monadic composition lets you compose such promises together, much like you would compose IO actions.然后标准的一元组合让您可以将这些承诺组合在一起,就像您组合IO动作一样。

With me so far?跟我到现在? Ok, good.好的,很好。

Now let's look at how might we implement a bind (aka >>= ).现在让我们看看如何实现绑定(又名>>= )。 The signature of >>= is the following: >>=的签名如下:

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

Now let's specialize this to functions.现在让我们将其专门用于函数。 Let's say, for now, that m ~ (->) Bool .现在,假设m ~ (->) Bool Then we have:然后我们有:

(>>=) :: (->) Bool a -> (a -> (->) Bool b) -> (->) Bool b

Or, if we rewrite the (->) constructor in infix form, we get:或者,如果我们以中缀形式重写(->)构造函数,我们得到:

(>>=) :: (Bool -> a) -> (a -> (Bool -> b)) -> (Bool -> b)

So you see - the bind takes a "promise of a ", then a function from a to a "promise of b ", and then returns a "promise of b ".所以你看 - 绑定接受“ a的承诺”,然后是 function 从a到“ b的承诺”,然后返回“ b的承诺”。 The implementation, then, is trivial:那么,实现是微不足道的:

(>>=) :: (Bool -> a) -> (a -> (Bool -> b)) -> (Bool -> b)
(>>=) promiseOfA f = \theBool -> f (promiseOfA theBool) theBool

here we create a new "promise", which is a function that takes Bool , and what this "promise" does is pass the given Bool to the "promise of a ", then passes the resulting a to the function f , which returns a "promise of b ", to which we then pass the same Bool to finally obtain the resulting b .这里我们创建一个新的“promise”,它是一个带Bool的 function ,这个“promise”所做的是将给定的Bool传递给“promise of a ”,然后将得到a传递给 function f ,它返回 a “promise of b ”,然后我们将相同的Bool传递给它,最终获得结果b

Pay attention here: see how theBool is used twice - first passed to promiseOfA and then passed again to the result of f ?注意这里:看看theBool是如何使用两次的——首先传递给promiseOfA ,然后再次传递给f的结果? That's where "reducing of the number of arguments" happens.这就是“减少参数数量”发生的地方。 This is where we pass the argument twice.这是我们两次传递参数的地方。

But of course, this doesn't have to work just for Bool s.但是,当然,这不仅仅适用于Bool s。 Any input type is fair game.任何输入类型都是公平的游戏。 So we can generalize it like this:所以我们可以这样概括:

(>>=) :: (input -> a) -> (a -> (input -> b)) -> (input -> b)
(>>=) promiseOfA f = \i -> f (promiseOfA i) i

(compare with the actual definition from the standard library ). (与标准库中的实际定义进行比较)。

Phew!呸!

Ok, now we are finally ready to look at your original example.好的,现在我们终于准备好查看您的原始示例了。 First, look at reverse .首先,看reverse Since it's a function, we can look at it as a monadic value in the monad (->) [a] - that is, a "promise of [a] ", where the "input" is also [a] .由于它是 function,我们可以将其视为 monad (->) [a]中的一元值 - 即“ [a]的承诺”,其中“输入”也是[a]

Then, the signature of (==) :然后, (==)的签名:

(==) :: Eq x => x -> x -> Bool

(note that I replaced a with x on purpose: not to confuse it with the a from reverse 's signature - they are two different a s, don't have to be the same type) (请注意,我故意将a替换为x :不要将其与a from reverse的签名混淆 - 它们是两个不同a ,不必是相同的类型)

We can look at this signature as a function that takes an x and returns another function of type x -> Bool .我们可以将此签名视为一个 function ,它接受一个x并返回另一个x -> Bool类型的 function 。 So:所以:

(==) :: x -> (x -> Bool)

This can be seen as second argument of bind, the one of type a -> mb .这可以看作是 bind 的第二个参数,即a -> mb类型的参数。 In order to see it like that, we need to say that a ~ x and m ~ (->) x and b ~ Bool .为了这样看,我们需要说a ~ xm ~ (->) xb ~ Bool So the monad in question here is (->) x - ie a "promise" with an input of type x .所以这里讨论的单子是(->) x - 即输入类型为x的“承诺”。

But wait!可是等等! In order to bind this function to reverse , they need to be in the same monad!为了将此 function 绑定到reverse ,它们需要在同一个 monad 中! This means that x ~ [a] .这意味着x ~ [a] And this in turn means that the type of (==) gets pinned to:这反过来意味着(==)的类型被固定到:

(==) :: [a] -> ([a] -> Bool)

And so, when we call (>>=) passing it reverse as first argument and (==) as second, we get:因此,当我们调用(>>=)将其reverse作为第一个参数并将(==)作为第二个参数时,我们得到:

reverse >>= (==) 
= \i -> (==) (reverse i) i   -- by my definition above
= \i -> reverse i == i

I think this will be an extension to a previous answer of mine in which i had explained how is a function a functor and applicative.我认为这将是对我之前的答案的扩展,我在其中解释了 function 如何成为仿函数和应用程序。 Let me start with the same sentence.让我从同一句话开始。

We may consider functions ( (->) r ) as a context with a contained value revealed once applied.我们可以将函数( (->) r )视为具有包含值的上下文,一旦应用就会显示出来。 Here we have reverse function which contains a reversed list but we don't get it until we apply it to a list.在这里,我们有reverse function ,其中包含一个反向列表,但直到我们将其应用于列表时才得到它。 Let's remember the type signature of bind.让我们记住 bind 的类型签名。

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

Here ma is reverse function and the a inside m is the reversed list.这里mareverse function 而m里面的a是反向列表。 (==) is the (a -> mb) and mb is a -> Bool . (==)(a -> mb)并且mba -> Bool So >>= returns an mb which is just a function which takes a list and returns a Bool .所以>>=返回一个mb ,它只是一个 function ,它接受一个列表并返回一个Bool This is consistent because we are in the function monad so it takes a monad (function) and returns another one.这是一致的,因为我们在 function monad 中,所以它需要一个 monad(函数)并返回另一个。 Only when we invoke the returned one with a list the whole mechanism runs.只有当我们用一个列表调用返回的那个时,整个机制才会运行。 Both ma and mb gets applied to the provided list. mamb都应用于提供的列表。

Manually implementing the monad instance of (->) r would be like手动实现(->) r的单子实例就像

return x = \_ -> x -- aka const
f >>= g = \r -> g (f r) r

If we look into this carefully we notice that >>= is actually the flipped version of <*> for (->) r type.如果我们仔细研究,我们会注意到>>=实际上是<*>的翻转版本,用于(->) r类型。 But be careful when i say fllipped it doesnt mean g <*> f == f >>= g .但是当我说翻转它并不意味着g <*> f == f >>= g时要小心。

g <*> f = \x -> g x (f x)
f >>= g = \x -> g (f x) x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM