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

Почему объявления подписи типа Haskell имеют несколько стрелок?

Извините, это написано плохо, но это трудно описать.

Думаю, я просто перейду к примеру:

add                     :: Integer -> Integer -> Integer
add x y                 =  x + y

почему:

:: Integer -> Integer -> Integer

вместо:

:: Integer, Integer -> Integer

стрелка - это оператор отображения типа функции", а не какой-то разделитель, no?

4b9b3361

Ответ 1

Из-за карри. Подумайте о типе этого:

add 3 :: Integer -> Integer

Если вы даете add одно число, оно возвращает функцию, которая сопоставляет Integer с другим целым числом. Поэтому вы можете сделать это:

map (add 3) [1..10]

Не имеет смысла рассматривать аргументы по-разному от возвращаемых типов с частичным приложением.

ИЗМЕНИТЬ, чтобы уточнить

Я думаю, что bheklilr хорошо понимает, что подпись типа может быть прочитана следующим образом

add :: Integer -> (Integer -> Integer)

Мы можем взять функцию с большим количеством аргументов, zipWith3, потому что это единственный, о котором я действительно могу думать.

zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]

Если мы просто прочитаем, что для этого требуется функция, которая принимает 3 значения и возвращает четвертый, а затем 3 списка этих значений соответственно и возвращает список четвертого значения. Попробуйте это.

add3 :: Int -> Int -> Int -> Int
add3 a b c = a + b + c

Prelude>zipWith3 add3 [1] [2] [3]
[6]

Хотя в этом случае все значения имеют тип Int, он все еще демонстрирует точку.

Теперь, если мы не дадим все списки? Что, если мы не дадим ему никаких списков, просто add3.

zipWith3 add3 :: [Int] -> [Int] -> [Int] -> [Int]
zipWith3 add3 :: [Int] -> ([Int] -> [Int] -> [Int])
zipWith3 add3 :: [Int] -> [Int] -> ([Int] -> [Int])

Итак, теперь у нас есть функция, которая принимает 3 списка и возвращает список. Но это также функция, которая принимает список, возвращает функцию, которая принимает 2 списка и возвращает список. Нет никакого способа отличить их.

(zipWith3 add3) [1,2] [3,4] [5,6] :: [Int]
(zipWith3 add3) [1,2] :: [Int] -> [Int] -> [Int]

Посмотрите, где я собираюсь? Нет никакого различия между аргументами, являются возвращаемые типы.

Ответ 2

Моим "ага" был момент, когда я понял, что

map :: (a -> b) -> [a] -> [b]

на самом деле выглядит более естественным, когда вы явно группируете:

map :: ( a -> b )
    -> ([a]->[b])

он принимает функцию и возвращает функцию list. С нечетким определением эта группировка не совсем работает, не так ли?

map :: ( a -> b
       ,[a])->[b]

но эта группировка в значительной степени является "более глубоким", более полезным способом мышления о функции. В частности, если вы его обобщите:

class Functor f where
  fmap :: (a->b) -> f a->f b

A functor выполняет две вещи: принимает некоторый тип (например, a или b) и связывает его с другим типом (f a, f b). Но он также принимает морфизм (a -> b) и связывает его с другим морфизмом (f a -> f b).

Ответ 3

Подпись типа более корректно читается как

add :: Integer -> (Integer -> Integer)

Что сильно отличается от

add :: (Integer -> Integer) -> Integer

Первая обозначает функцию, которая принимает Integer и возвращает новую функцию, которая принимает Integer и возвращает Integer. Это облегчает частичное применение функций, поэтому такие вещи, как

(+) :: Int -> Int -> Int  -- specialized to Int

(1 +) :: Int -> Int

> map (1 +) [1..10]
[2,3,4,5,6,7,8,9,10,11]