2015-12-07 14 views
20

Take podpis typu z fmap (metoda Functor) jako przykład:Jakie jest znaczenie nawiasów w sygnaturach typu Haskella?

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

Jak to jest, że różni się od po podpisaniu typu?

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

Czy istnieje nawet różnica między tymi dwoma typami podpisów?

+2

Jak kwestia w tekście i na pytanie w tytule powiązane? Nawiasy w sygnaturach typów * nie * oznaczają specjalną semantykę, są one dla pierwszeństwa, tak jak wszędzie indziej w Haskell, dlatego dwa fragmenty są * nie * równoważne. –

+2

@ JörgWMittag: Zgadzam się, że te dwa pytania nie są * równoważne * - i w rzeczywistości mają różne odpowiedzi - ale jestem zaskoczony, że znalazłeś je * bez związku *. Obydwa dotyczą znaczenia nawiasów w podpisach typu; odpowiedź "tak" na pytanie w tytule prawdopodobnie pociągnąłaby za sobą "tak" odpowiedź na pytanie w tekście; a dobra odpowiedź na którekolwiek z tych pytań prawdopodobnie pośrednio (lub nawet jednoznacznie) odpowie na inne. – ruakh

+1

Nawiasy w sygnaturach typu oznaczają pierwszeństwo, podobnie jak w terminach. Pierwszeństwo jest czysto * składniową * własnością, nie ma znaczenia semantycznego.Tak więc nawiasy w sygnaturach typu nie są ani specjalnymi, ani semantykami, a * to * dlatego te dwa fragmenty nie są równoważne, tak jak "a + b * c" i "(a + b) * c" nie są równoważne, także dlatego, że pierwszeństwa syntaktycznego, a nie z powodu semantyki. –

Odpowiedz

29

Tak, jest różnica, ponieważ the -> type constructor is right-associative. Innymi słowy,

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

odpowiada

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

Podpis ten typ oznacza funkcję, która przyjmuje parametr typu a i wraca do funkcji, która z kolei zajmuje się parametr typu b i zwraca funkcja, która sama pobiera parametr typu f a i zwraca wartość typu f b.

Z drugiej strony,

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

oznacza funkcję, która przyjmuje parametr typu a -> b (czyli funkcja, która przyjmuje parametr typu a i zwraca wartość typu b) i zwraca funkcję, która sama pobiera parametr typu f a i zwraca wartość typu f b.

Oto wymyślony przykład, który ilustruje różnicę między dwoma podpisami typ:

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] 
13

Tak,

(a -> b) -> ... 

oznacza "podana funkcja, która trwa do b ...". Chociaż ta

a -> b -> ... 

znaczy „biorąc pod uwagę pewne ai trochę b ...”

6

Tak, (a -> b) oznacza jeden argument, który jest funkcją z podpisem a -> b, natomiast a -> b -> ... oznacza dwa argumenty.