Подтвердить что ты не робот

Как я могу понять ": t ((==) <*>)" в Haskell?

Я новичок в Haskell, здесь возникают проблемы с <*>:

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

Как я могу понять это и как его можно вывести?

4b9b3361

Ответ 1

Отказ от ответственности: это не идиоматический код 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

Скобки вокруг (->) означают, что они используются в префиксной форме (что необходимо для синтаксических соображений при определении экземпляров класса, подобных этому). Если вы расширите его до формы infix, вы получите:

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

В вашем конкретном примере первым аргументом <*> является оператор (==), который имеет следующий тип:

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

Поэтому, если мы передадим его в (<*>), то компилятор может больше узнать о типах r, a и b в сигнатуре для (<*>):

 (<*>) ::        (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)

Когда мы окружаем (==) круглыми скобками, это означает, что мы используем его в префиксной нотации. Это означает, что если мы удалим круглые скобки и изменим их на нотацию infix, получим:

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

Это эквивалентно:

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

Таким образом, это означает, что ваша функция принимает два параметра: g и r, а затем видит, если r равно g r.