【问题标题】:How can I understand ":t ((==) <*>)" in Haskell?我如何理解 Haskell 中的“:t ((==) <*>)”?
【发布时间】:2013-09-19 17:48:26
【问题描述】:

我是 Haskell 的新手,在这里遇到&lt;*&gt; 的麻烦:

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

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

【问题讨论】:

    标签: haskell types ghc type-inference


    【解决方案1】:

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

    优先考虑的是&lt;*&gt; 的“运算符部分”。当您看到一个运算符仅应用于一个称为部分的参数时。下面是一个更常见的运算符部分的示例:

    (1 +) :: Int -> Int
    

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

    \x -> 1 + x
    

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

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

    接下来你需要了解&lt;*&gt; 在做什么。这是Applicative 类型类的成员:

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

    这意味着&lt;*&gt; 被重载以适用于实现Applicative 的任何类型。 Applicative 类型的一个实例是((-&gt;) r)

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

    (-&gt;) 周围的括号表示以前缀形式使用(在定义像这样的类实例时,出于语法原因,这是必要的)。如果将其扩展为中缀形式,则会得到:

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

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

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

    因此,如果我们将其传递给(&lt;*&gt;),编译器可以在(&lt;*&gt;) 的签名中推断出更多关于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`
    

    所以当我们提供(==) 作为(&lt;*&gt;) 的第一个参数时,我们会得到这个推断类型:

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

    您可以省略右括号,因为(-&gt;) 是右结合的,并将e 更改为a 以给出您得到的最终签名:

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

    但这实际上在做什么?要了解我们需要了解(&lt;*&gt;) 是如何为((-&gt;) r)Applicative 实例定义的:

    (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

    【讨论】:

    • @user2407038 不完全是。考虑函数g r = r * 10g 0 == 0,但这并不意味着g 是身份函数! ((==) &lt;*&gt;) 接受一个函数一个输入,并测试该函数是否将该特定输入映射到自身。
    • @Ben 不。它只需要一个特定的r,将g 应用一次,然后查看它是否与原始r 匹配。它没有对所有rs 进行测试。相反,它只检查一个特定的r 和一个函数g 的应用程序。
    • @shree.pat18 不完全是因为它不会得到/给你一个固定点。它只告诉您您提供的值r 是否是g 的固定点。
    • @GabrielGonzalez “免责声明:这不是惯用的 Haskell 代码。”是什么意思?
    • @erisco 编写这样的代码不是一个好习惯。这是不必要的混乱
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    相关资源
    最近更新 更多