2017-02-21 11 views
2

У меня есть следующий тип подписи:Почему тип Char -> Char -> Char

*Main Lib> let f :: a -> a -> a -> a; f = undefined 
*Main Lib> let x :: Char; x = undefined 

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

*Main Lib> :t f x 
f x :: Char -> Char -> Char 

Почему не

Char -> Char -> Char -> Char 

из-за первый параметр уже с x замещенным?

+0

Поскольку тип применения функции является '(а -> б) -> а -> b', а не' (а - > b) -> a -> (a -> b) '. – user2407038

ответ

3

Да, точно. Рассмотрим другую функцию, length :: [a] -> Int. Если вы попросите типа length "abc", вы получаете Int:

Prelude> :t length "foo" 
length "foo" :: Int 

В общем, если у вас есть функция f :: A -> B и аргумент x :: A, то f x :: B.

В вашем случае мы имеем f :: a -> (a -> a -> a) (специализированный для a = Char), так и A = CharB = Char -> Char -> Char.

(a -> a -> a -> a такая же, как a -> (a -> a -> a), потому что -> типов является правильным ассоциативным.)

+0

Большое спасибо за вашу помощь. –

2

Это потому, что f x - это функция f с первым параметром, который уже применяется, поэтому для получения результата требуется еще два. Это называется currying.

В Haskell на самом деле нет такой функции, как функция с тремя параметрами. a -> a -> a -> a означает «Функция, которая принимает a и возвращает функцию, которая принимает a и возвращает функцию, которая принимает a и возвращает a».

Это много глотка, чтобы выписать, так что просто поставьте несколько парнеров, и он становится яснее: a -> (a -> (a -> a)).

So f x дает вам a -> (a -> a). Так получилось, что синтаксис Haskell позволяет вам сказатьи действовать так, как будто вы вызываете функцию с тремя параметрами и что компилятор достаточно умен, чтобы не создавать все эти промежуточные карри, если это не нужно.