W dokumentacji dla Data.Functor następujące dwa są określone jako prawa funktora, do których mają się stosować wszystkie funktory.Czy prawa funktora udowadniają całkowitą ochronę struktury?
fmap id == id
fmap (f . g) == fmap f . fmap g
Droga moja intuicja mówi mi funktory powinien pracować jest to, że powinny one być „struktura zachowania”, czyli innymi słowy, jeśli masz funkcję f :: a -> b
i to odwrotność g :: b -> a
następnie
fmap f . fmap g == id
Nie byłem w stanie wymyślić implementacji fmap
, która byłaby zgodna z pierwszymi dwoma prawami i naruszała drugą, ale to nie jest żaden dowód. Czy ktoś może mnie oświecić?
+1 Ostatnio zmagałem się z podobnymi pytaniami. Jest jedna rzecz, która przeszkadza mi w tym intuicyjnym sformułowaniu: co z funkcjami bez odwrotności? Z pewnością 'fmap (const" Foo ")' jest w pewnym sensie zachowujący strukturę? – duplode
Tak, ale nie możesz użyć 'const" Foo "' do potwierdzenia prawdziwości prawa - musisz wybrać inną funkcję. Nie mówię, że funktory są ważne tylko w odniesieniu do funkcji, jakie obejmuje prawo. – kqr
@duplode wyobrazić sobie drzewo danych drzewa a = liść | Węzeł a [Drzewo a] '. Kiedy mówimy o "strukturze" 't :: Tree a', myślimy o tym, jak wiele poddrzew ma każdy węzeł i jak są one zorganizowane. Można więc powiedzieć, że jesteśmy zainteresowani 't' z _okolwiek w jego węzłach_. I rzeczywiście możemy to uchwycić za pomocą 'fmap (const()) t'. – fizruk