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

В чем смысл круглых скобок в подписях типа Haskell?

В качестве примера возьмем сигнатуру типа fmap (метод Functor):

(a -> b) -> f a -> f b

Как это отличается от следующей сигнатуры типа?

a -> b -> f a -> f b

Есть ли разница между этими двумя типами подписей?

4b9b3361

Ответ 1

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

a -> b -> f a -> f b

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

a -> (b -> (f a -> f b))

Эта сигнатура типа обозначает функцию, которая принимает параметр типа a и возвращает функцию, которая сама принимает параметр типа b и возвращает функцию, которая сама принимает параметр типа f a и возвращает значение типа f b.

С другой стороны,

(a -> b) -> f a -> f b

обозначает функцию, которая принимает параметр типа a -> b (т.е. функцию, которая принимает параметр типа a и возвращает значение типа b) и возвращает функцию, которая сама принимает параметр типа f a и возвращает значение типа f b.

Вот надуманный пример, иллюстрирующий разницу между двумя типами сигнатур:

f :: (Int -> Bool) -> [Int] -> [Bool]
f = map

g :: Int -> Bool -> [Int] -> [Bool]
g n b = map (\n' -> (n' == n) == b)

λ> let ns = [42, 13, 42, 17]

λ> f (== 42) ns
[True,False,True,False]

λ> g 42 True ns
[True,False,True,False]

λ> g 42 False ns
[False,True,False,True]

Ответ 2

Да,

(a -> b) -> ...

означает "заданная функция, которая берет a в b...". Хотя этот

a -> b -> ...

означает "с учетом некоторых a и некоторых b..."

Ответ 3

Да, (a -> b) означает один аргумент, который является функцией с сигнатурой a -> b, тогда как a -> b -> ... означает два аргумента.