2012-04-24 18 views
20

rodzaj FMapy w funktora jest:mylić o funkcji jako wystąpienie funktora w Haskell

fmap :: Functor f => (a -> b) -> f a -> f b 

wygląda, najpierw zastosować funkcję (a -> b) do parametru fa stworzenia wynik typ b, a następnie zastosować do niej f, a wynik jest fb

użyciu Może na przykład:

fmap show (Just 1) 
result is : Just "1" 

samo jak powiedzenie:

Just (show 1) 

ale kiedy (->) jest używany jako funktor (w Control.Monad.Instances)

import Control.Monad.Instances 
(fmap show Just) 1 
result is : "Just 1" 

że jest to tylko zastosować pierwszy, a następnie nakłada się pokazać. w innym przykładzie wynik jest taki sam:

fmap (*3) (+100) 1 
result is 303 

dlaczego nie * 3 pierwsze, to +100?

Odpowiedz

22

typ FMapy w funktora jest:

fmap :: Functor f => (a -> b) -> f a -> f b 

wygląda na to, po pierwsze zastosowania funkcji (A -> B) parametru FA stworzyć efekt typu B, a następnie zastosować f nią , a wynikiem jest fb

To jest typ fmap, ale twoja interpretacja tego, co oznacza ten typ, jest błędna.

Wydaje się, że zakładasz, że f a ma jeden parametr i że ten parametr ma typ a.

Rozważmy xs :: [a]:

  • Może xs = [].
  • Być może xs = [x1].
  • Być może xs = [x1, x2].
  • ...

Typ f a to funktor f z jednego parametru typu a. Jednak wartości o wartości typu f a niekoniecznie przyjmują postać F x, jak widać z pierwszego i trzeciego przypadku powyżej.

Rozważmy teraz fmap f xs:

  • Może fmap f xs = [].
  • Być może fmap f xs = [f x1].
  • Być może fmap f xs = [f x1, f x2].
  • ...

Nie koniecznie zastosować f na wszystkich (pierwszym przypadku)! Lub możemy zastosować go więcej niż jeden raz (trzeci przypadek).

Co możemy zrobić, to wymienić rzeczy typu a na rzeczy typu b. Ale pozostawiamy większą strukturę w stanie nienaruszonym - nie dodaje się nowych elementów, nie usuwa się żadnych elementów, ich kolejność pozostaje niezmieniona.


Teraz pomyślmy o funktorze (c ->). (Pamiętaj, że funktor przyjmuje tylko jeden parametr, więc wejście do (->) jest poprawione.)

Czy c -> a zawiera nawet a? Może w ogóle nie zawierać żadnych a s, ale może jakoś magicznie wydobyć go z powietrza, gdy damy mu c. Ale wynik z fmap ma typ c -> b: musimy tylko dostarczyć b z tego, gdy jesteśmy przedstawieni z c.

Możemy więc powiedzieć, fmap f x = \y -> f (x y).

W tym przypadku stosujemy f na żądanie - za każdym razem, gdy funkcja, którą zwracamy, zostanie zastosowana, zostanie również zastosowana f.

+1

tak, twoja odpowiedź jest świetna! Popełniłem wielki błąd. Dziękuję Ci bardzo. –

+0

Mylę "parametr typu" z konkretnym parametrem –

2
fmap :: Functor f => (a -> b) -> f a -> f b 

Pamiętaj, że f może być również konstruktorem typów. Rozumiem to (dla najprostszych przypadków), że funkcja pobiera coś z rodzaju a owiniętego w f i przekształca ją w coś typu b owiniętego w f za pomocą funkcji a -> b.

W drugim przykładzie wykonujesz (fmap show Just) 1. Jest to rodzaj

Prelude> :t fmap show Just 
fmap show Just :: (Show a, Functor ((->) a)) => a -> String 

to jest w przeciwieństwie do poprzedniego

Prelude> :t fmap show (Just 1) 
fmap show (Just 1) :: Maybe String 

Różnica polega na tym, że w pierwszym Just jest konstruktor typu, natomiast Just 1 jest przykładem typu. fmap jest odpowiednio generyczny, tak aby miał znaczenie dla obu.

+1

W "Pamiętaj, że f może być także konstruktorem typu" prawdopodobnie oznacza "musi" zamiast "może również". –

+0

'Just' nie jest konstruktorem typu, jest konstruktorem wartości lub danych. 'Maybe' jest konstruktorem typu. –

5

fmap dla (->) jest zdefiniowany jako fmap = (.). Tak, (fmap f g) x jest (f . g) x jest f (g x). W twoim przypadku (*3) ((+100) 1), co równa się 3 * (100 + 1), co daje 303.

26

Instancja fmap dla (->) r (tj. Funkcje) jest dosłownie samą kompozycją. Od the source itself:

instance Functor ((->) r) where 
    fmap = (.) 

Tak w przykładzie, można po prostu zastąpić fmap z (.) i zrobić pewne przekształcenia

fmap (*3) (+100) 1 => 
(.) (*3) (+100) 1 => 
(*3) . (+100) $ 1 => -- put (.) infix 
(*3) (1 + 100)  => -- apply (+100) 
(1 + 100) * 3   -- apply (*3) 

Oznacza to, fmap dla funkcji komponuje je od prawej do lewej (dokładnie taką samą jako (.), co jest sensowne, ponieważ jest to (.)).

Aby spojrzeć na to w inny sposób (na (podwójne) potwierdzenie!), Możemy użyć podpisu wpisz:

-- general fmap 
fmap :: Functor f => (a -> b) -> f a -> f b 

-- specialised to the function functor (I've removed the last pair of brackets) 
fmap :: (a -> b) -> (r -> a) -> r -> b 

Więc najpierw wartość typu r (trzeci argument) musi zostać przekształcona w wartość typu a (przez funkcję r -> a), tak że funkcja a -> b może przekształcić go w wartość typu b (wynik).

+0

Dziękuję, to jest miła jasna definicja! –

+1

tak, fmap :: (a -> b) -> (r -> a) -> r -> b, to wyjaśnia wiele, dziękuję –

14

Wymaga zdefiniowania , aby można było opracować typy. Jak podkreślił, rodzaj fmap jest:

fmap :: Functor f => (a -> b) -> f a -> f b 

Rozważmy przypadek, gdy funktor f jest ((->) c)

(Uwaga: dalszy faktycznie jak napisać to jako (c ->), tj funkcje od c, ale Haskell nie pozwala nam to zrobić.)

Następnie f a jest rzeczywiście ((->) c a), co jest równoważne (c -> a) i podobnie dla, więc mamy:

fmap :: (a -> b) -> (c -> a) -> (c -> b) 

to trzeba mieć dwie funkcje:

  • f :: a -> b
  • g :: c -> a

i budowy nowej funkcji

  • h :: c -> b

Ale istnieje tylko jeden sposób, aby to zrobić: trzeba zastosować g pierwszy dostać coś typu a, a następnie zastosować f dostać coś typu b, co oznacza, że ​​masz zdefiniować

instance Functor ((->) c) where 
    fmap f g = \x -> f (g x) 

lub więcej zwięźle

instance Functor ((->) c) where 
    fmap = (.) 
0

Aby utworzyć typ funkcji, potrzebne są 2 parametry rodzajowe dla (->), czyli pojedynczy typ argumentu wejściowego i typ zwracany.

Funktor może przyjmować tylko 1 typ parametru, więc musisz przybić typ argumentu wejściowego (ponieważ jest to pierwszy od lewej do prawej), co powoduje, że typ zwracany funkcji jest parametrem typu Funktor.

Tak więc dla funkcji (Funktor) a-> b, musisz nadać fmap funkcję ff typu b-> xxx inną niż a-> xxx do pracy, a to oznacza, że ​​funkcja ff może być zastosowana tylko po a-> b ma zastosowanie.