2010-09-22 7 views
6

Weź szybki okiem na następnej sesji interaktywnej w GHCi:

 
Prelude> import Control.Applicative 
Prelude Control.Applicative> (+1) <$> [1,2] 
[2,3] 
Prelude Control.Applicative> (+1) <$> (1,2) 
(1,3) 

Chyba nie jest dobry powód, dla zachowania <$> dotyczących parami, ale nie był w stanie znaleźć tak daleko, tak:

dlaczego zdefiniowano <$> (lub `fmap`), aby działał tylko na drugim elemencie pary, a nie na obu wartościach?

+0

To pytanie jest związane: http://stackoverflow.com/questions/3155469/help-haskell-functors-sink-in/3164774#3164774 –

Odpowiedz

15

<$> (aka fmap) jest członkiem klasy funktora tak:

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

Więc cokolwiek f to musi być sparametryzowanego typu z jednego typu argumentu. Listy są jednym z takich typów, jeśli zapisano w ich prefiksie [] ([] a jest taki sam jak [a]). Więc instancja dla list jest:

instance Functor [] where 
    -- fmap :: (a -> b) -> [] a -> [] b 
    fmap = map 

Pary mogą być również napisany w formie przedrostka: (,) a b jest taka sama jak (a, b). Zastanówmy się więc, co robimy, jeśli chcemy mieć instancję Functor zawierającą pary. Możemy nie można zadeklarować instance Functor (,), ponieważ konstruktor para (,) ma dwa rodzaje - i mogą być różne typy! Co możemy zrobić, to zadeklarować instancję dla (,) a - to typ, który potrzebuje tylko jednego typu więcej:

instance Functor ((,) a) where 
    -- fmap :: (b -> c) -> (,) a b -> (,) a c 
    fmap f (x, y) = (x, f y) 

Mam nadzieję, że widać, że definicja FMapy jest tylko jeden sensowny możemy dać. Odpowiedź na pytanie, dlaczego instancja funktora działa na drugim elemencie pary, polega na tym, że typ drugiego elementu pojawia się na końcu! Nie możemy łatwo zadeklarować instancji funktora, która działa na pierwszym elemencie w parze. Nawiasem mówiąc, to generalizuje się na większe krotki, np. czteroosobowy (,,,) a b c d (aka (a, b, c, d)) może mieć również wystąpienie Functor na ostatniej pozycji:

instance Functor ((,,,) a b c) where 
    -- fmap :: (d -> e) -> (,,,) a b c d -> (,,,) a b c e 
    fmap f (p, q, r, s) = (p, q, r, f s) 

nadzieję, że pomoże to wyjaśnić wszystko!

+0

Wpisano szybciej niż ja ... argh – fuz

+0

błąd: przykłady powinny powiedzieć 'instance Functor []' zamiast 'class'. – hzap

+0

Dzięki - teraz naprawione –

2

Domyślam się, że krotka nie musi być jednorodna, co oznacza, że ​​oba typy mogą być różne. Jeśli chcesz mieć jednorodną krotkę, możesz użyć listy, a następnie zadziała Fmap.

Jak można się spodziewać, że (+1) ("Hello", 2) zadziała?

Prelude> import Control.Applicative 
Prelude Control.Applicative> (+1) <$> ("hello",2) 
("hello",3) 

To tylko praca, ale nie ma specjalnego zachowania, gdy oba typy są takie same. Przy okazji nie wiem, dlaczego druga wartość nie jest używana, a nie pierwsza, ale w każdym razie możesz użyć tylko jednej wartości.

3

Rozważmy definicję Functor typeclass:

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

Oczywiście f ma rodzaj * -> *. Możesz więc zadeklarować tylko instancje dla typu danych, który ma rodzaj * -> *.Co można zrobić, robi pewne rzeczy tak:

instance Functor (,) where 
    fmap :: (a -> b) -> (,) a -> (,) b 

To będzie działać na partitial applicated krotki i jest bardzo niepraktyczny. Tak zdefiniowany wystąpienie takiego:

instance Functor ((,) a) where 
    fmap :: (b -> c) -> (,) a b -> (,) a c 
    fmap f (x,y) = (x,f y) 

W skrócie: nie jest to możliwe w zwykłym Haskell 98 (choć wierzę tht istnieje rozszerzenie składnię tego), aby określić wystąpienie jako

Co ty może zrobić, definiuje własną krotkę:

data T a = T a a 

instance Functor T where 
    fmap f (T a b) = T (f a) (f b) 

Następnie możesz robić, co chcesz. Widzisz, ponieważ ten rodzaj to * -> * zamiast * -> * -> *, wszystko jest w porządku.