Как рассуждать о частично применяемом методе объединения - PullRequest
0 голосов
/ 03 апреля 2019

Я пытаюсь понять, как рассуждать о типах для частичного применения цепочки методов.Я не понимаю, почему:
:t (+)(+2) это (a->a)->a->a
или почему:
:t (+)(+) это (a->a->a)->a->a->a

Я имею в виду для первого примера, я не понимаю, когдаглядя на (+) нужно ли мне смотреть, что ему нужно a->a->a или какой метод стоит перед ним (+2) (для которого нужен a).

Во втором примере я знаю первый(+) нужно a->a->a, но как только я полностью заполнил первый метод, зачем второму снова нужны те же параметры?

1 Ответ

3 голосов
/ 03 апреля 2019

Вы на самом деле немного неправильно сообщили тип. Там есть некоторые важные ограничения класса типов:

(+)(+2) :: (Num a, Num (a -> a)) => (a -> a) -> a -> a

Так откуда это? Это на самом деле довольно просто. Во-первых, сигнатура типа (+) равна

(+) :: Num a => a -> a -> a

Или переписать, чтобы сделать карри явным:

(+) :: Num a => a -> (a -> a)

Между тем, тип (+2) (который является результатом выполнения именно этого частичного приложения):

(+2) :: Num a => a -> a

Теперь, когда вы делаете (+)(+2), вы (частично) применяете функцию (+) к функции (+2). То есть мы рассматриваем (+2) как первый аргумент (+). Чтобы это работало, его тип - Num a => a -> a - должен быть экземпляром Num. Вот почему у нас есть еще одно ограничение типа: a -> a должен быть экземпляром Num. (Это никогда не происходит по умолчанию, но вы можете определить свой собственный экземпляр для числовых функций - обычно он применяет обе функции для ввода и добавляет результаты.)

Итак, давайте специализируем сигнатуру типа (+) для случая, когда она применяется к функции (a -> a) (которая, как я только что сказал, сама должна быть экземпляром Num, а также a сам). Мы получаем:

(+) :: (Num a, Num (a -> a)) => (a -> a) -> (a -> a) -> (a -> a)

или с указанием карри:

(+) :: (Num a, Num (a -> a)) => (a -> a) -> ((a -> a) -> (a -> a))

То есть он принимает функцию a -> a и возвращает «функцию высшего порядка» типа (a -> a) -> (a -> a). Таким образом, когда мы применяем это к (+2), мы получаем именно такую ​​функцию высшего порядка:

(+)(+2) :: (Num a, Num (a -> a)) => (a -> a) -> (a -> a)

Это именно то, что сообщается, так как последняя пара скобок не нужна. (Это из-за карри снова.)

Второй случай полностью аналогичен, за исключением того, что функция, к которой вы применяете, является a -> a -> a, а не a -> a.

...