如何在Haskell中理解“:t((==)< *>)”?

时间:2013-09-15 14:21:09

标签: haskell types ghc type-inference

我是Haskell的新手,这里遇到了<*>的问题:

((==) <*>) :: Eq a => (a -> a) -> a -> Bool

我如何理解这一点以及如何推断它?

1 个答案:

答案 0 :(得分:56)

免责声明:这不是惯用的Haskell代码。

优先考虑的第一件事是<*>的“运营商部分”。当您看到运算符仅应用于一个称为节的参数时。以下是更常见的运算符部分的示例:

(1 +) :: Int -> Int

这是一个部分将+应用于1的函数,为最后一个参数留出空间。它相当于:

\x -> 1 + x

因此,在您的代码示例中,<*>已部分应用于(==),因此我们将其扩展为:

((==) <*>)

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

接下来,您需要了解<*>正在做什么。这是Applicative类型类的成员:

class (Functor f) => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

这意味着<*>被重载以处理实现Applicative的任何类型。 Applicative类型的一个实例是((->) r)

instance Applicative ((->) r) where
    pure  :: a -> ((->) r) a
    (<*>) :: (->) r (a -> b) -> (->) r a -> (->) r b

围绕(->)的括号表示正在以前缀形式使用(在定义像这样的类实例时,这是出于语法原因所必需的)。如果你将它扩展为中缀形式,你会得到:

instance Applicative ((->) r) where
    pure  :: a -> r -> a
    (<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)

在您的具体示例中,<*>的第一个参数是(==)运算符,它具有以下类型:

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

因此,如果我们将其传递给(<*>),编译器可以在{{1}的签名中推断出有关rab类型的更多信息。 }}:

(<*>)

因此,当我们提供 (<*>) :: (r -> a -> b ) -> (r -> a) -> (r -> b) (==) :: Eq e => e -> e -> Bool | | | | | +-> `b` must be `Bool` | | | +------> `a` must be `e` | +-----------> `r` must also be `e` 作为(==)的第一个参数时,我们得到此推断类型:

(<*>)

您可以不使用正确的括号,因为((==) <*>) :: Eq e => (e -> e) -> (e -> Bool) 是右关联的,并将(->)更改为e以提供您获得的最终签名:

a

但这实际上是做什么的?要了解我们需要了解((==) <*>) :: Eq a => (a -> a) -> a -> Bool (<*>) Applicative实例的定义((->) r)

(f <*> g) r = f r (g r)

如果我们将f替换为(==),我们会得到:

((==) <*> g) r = (==) r (g r)

当我们用括号括起(==)时,这意味着我们在前缀表示法中使用它。这意味着如果我们删除括号并将其更改回中缀表示法,我们得到:

((==) <*> g) r = r == (g r)

这相当于:

((==) <*>) = \g r -> r == g r

这意味着您的函数需要两个参数:gr,然后查看r是否等于g r