Znaczenie funkcji izomorficznych

Krótkie pytanie: Jakie jest znaczenie funkcji izomorficznych w programowaniu (mianowicie w programowaniu funkcyjnym)?

Długie Pytanie: próbuję narysować kilka analogów między programowaniem funkcyjnym a pojęciami w teorii kategorii bazując na niektórych słowach, które słyszę od czasu do czasu. Zasadniczo staram się "rozpakować" ten żargon w coś konkretnego, co mogę potem rozwinąć. Wtedy będę mógł używać żargonu ze zrozumieniem tylko-co-do-cholery-mówię. Co zawsze jest miłe.

Jednym z tych terminów, które słyszę cały czas jest Izomorfizm , zakładam, że chodzi o rozumowanie o równoważności między funkcjami lub kompozycjami funkcyjnymi. Zastanawiałem się, czy ktoś mógłby podać jakieś spostrzeżenia na temat wspólnych wzorców, w których przydaje się właściwość izomorfizmu (w programowaniu funkcyjnym), oraz wszelkie uzyskane produkty uboczne, takie jak optymalizacje kompilatorów z rozumowania o izomorficzności funkcje.

Author: aculich, 2012-06-28

3 answers

Biorę mały problem z upvoted odpowiedzi dla izomorfizmu, ponieważ definicja kategorii teorii izomorfizmu nie mówi nic o obiektach. Aby zobaczyć dlaczego, przejrzyjmy definicję.

Definicja

Izomorfizm jest parą morfizmów (tj. funkcji), f i g, takich że:

f . g = id
g . f = id
Morfizmy te nazywane są morfizmami "iso". Wiele osób nie łapie, że "morfizm" w izomorfizmie odnosi się do funkcji, a nie obiektu. Jednakże, można by powiedzieć, że obiekty, które łączą, są "izomorficzne", co opisuje druga odpowiedź.

Zauważ, że definicja izomorfizmu nie mówi, co (.), id, albo = musi być. Jedynym wymogiem jest to, że niezależnie od tego, czym są, również spełniają prawa kategorii: {]}

f . id = f
id . f = f
(f . g) . h = f . (g . h)

Kompozycja (tzn. (.)) łączy dwa morfizmy w jeden morfizm i id oznacza pewien rodzaj przejścia "tożsamości". Oznacza to, że jeśli nasze izomorfizmy odwołują się do morfizm tożsamości id, wtedy można myśleć o nich jako o odwrotnościach siebie nawzajem.

Dla konkretnego przypadku, gdy morfizmy są funkcjami, to id jest zdefiniowana jako funkcja tożsamościowa:

id x = x

... i skład definiowany jest jako:

(f . g) x = f (g x)

... a dwie funkcje są izomorfizmami, jeśli odwołują się do funkcji tożsamościowej id, gdy je tworzymy.

Morfizmy a obiekty

Istnieje jednak wiele sposobów, w jakie dwa obiekty mogą być izomorficzny. Na przykład, biorąc pod uwagę następujące dwa typy:

data T1 = A | B
data T2 = C | D

Istnieją między nimi dwa izomorfizmy:

f1 t1 = case t1 of
    A -> C
    B -> D
g1 t2 = case t2 of
    C -> A
    D -> B

(f1 . g1) t2 = case t2 of
    C -> C
    D -> D
(f1 . g1) t2 = t2
f1 . g1 = id :: T2 -> T2

(g1 . f1) t1 = case t1 of
    A -> A
    B -> B
(g1 . f1) t1 = t1
g1 . f1 = id :: T1 -> T1

f2 t1 = case t1 of
    A -> D
    B -> C
g2 t2 = case t2 of
    C -> B
    D -> A

f2 . g2 = id :: T2 -> T2
g2 . f2 = id :: T1 -> T1

Dlatego lepiej jest opisać izomorfizm w kategoriach konkretnych funkcji odnoszących się do dwóch obiektów, a nie dwóch obiektów, ponieważ nie musi istnieć unikalna para funkcji między dwoma obiektami, które spełniają prawa izomorfizmu.

Należy również zauważyć, że nie wystarczy, aby funkcje były odwracalne. Na przykład, następujące pary funkcji nie są izomorfizmami:

f1 . g2 :: T2 -> T2
f2 . g1 :: T2 -> T2

Nawet jeśli podczas tworzenia f1 . g2 żadna informacja nie zostanie utracona, nie powrócisz do pierwotnego stanu, nawet jeśli stan końcowy ma ten sam typ.

Również izomorfizmy nie muszą być pomiędzy konkretnymi typami danych. Oto przykład dwóch kanonicznych izomorfizmów nie są między konkretnymi algebraicznymi typami danych i zamiast tego po prostu odnoszą się funkcje: curry i uncurry:

curry . uncurry = id :: (a -> b -> c) -> (a -> b -> c)
uncurry . curry = id :: ((a, b) -> c) -> ((a, b) -> c)

Używa do Izomorfizmy

Kodowanie Kościoła

Jednym z zastosowań izomorfizmów jest kodowanie typów danych jako funkcji. Na przykład Bool jest izomorficzne do forall a . a -> a -> a:

f :: Bool -> (forall a . a -> a -> a)
f True  = \a b -> a
f False = \a b -> b

g :: (forall a . a -> a -> a) -> Bool
g b = b True False

Sprawdź, że f . g = id i g . f = id.

Zaletą typów danych kodowania Kościoła jest to, że czasami działają szybciej (ponieważ kodowanie Kościoła jest stylem kontynuacyjno-przekazującym) i mogą być zaimplementowane w językach, które nie mają nawet obsługi algebraicznych typów danych w wszystkie.

Tłumaczenia Wdrożeń

Czasami próbuje się porównać implementację jakiejś biblioteki z implementacją innej biblioteki i jeśli można udowodnić, że są izomorficzne, to można udowodnić, że są równie potężne. Ponadto izomorfizmy opisują, jak przetłumaczyć jedną bibliotekę na drugą.

Na przykład, istnieją dwa podejścia, które umożliwiają definiowanie monad na podstawie podpisu functora. Jednym z nich jest darmowa monada, dostarczany przez pakiet free, a drugi to semantyka operacyjna, dostarczana przez pakiet operational. Jeśli spojrzeć na dwa podstawowe typy danych, wyglądają one inaczej, zwłaszcza ich Drugi konstruktor:]}
-- modified from the original to not be a monad transformer
data Program instr a where
    Lift   :: a -> Program instr a
    Bind   :: Program instr b -> (b -> Program instr a) -> Program instr a
    Instr  :: instr a -> Program instr a

data Free f r = Pure r | Free (f (Free f r))

... ale w rzeczywistości są izomorficzne! Oznacza to, że oba podejścia są równie potężne, a każdy kod napisany w jednym podejściu może być przetłumaczony mechanicznie na inne podejście za pomocą izomorfizmów.

Izomorfizmy, które nie są funkcje

Izomorfizmy nie ograniczają się do funkcji. Są one zdefiniowane dla dowolnych Category, a Haskell ma wiele kategorii. Dlatego bardziej przydatne jest myślenie w kategoriach morfizmów, a nie typów danych.

Na przykład Typ Lens (Z data-lens) tworzy kategorię, w której można komponować soczewki i mieć soczewkę tożsamościową. Używając więc naszego powyższego typu danych, możemy zdefiniować dwie soczewki, które są izomorfizmami:

lens1 = iso f1 g1 :: Lens T1 T2
lens2 = iso g1 f1 :: Lens T2 T1

lens1 . lens2 = id :: Lens T1 T1
lens2 . lens1 = id :: Lens T2 T2

Zauważ, że istnieją dwa izomorfizmy w grze. Jednym z nich jest izomorfizm, który jest używany do budowy każdej soczewki (tj. f1 i g1) (i dlatego też Ta funkcja konstrukcyjna nazywa się iso), a wtedy same soczewki są również izomorfizmami. Zauważ, że w powyższym sformułowaniu kompozycja (.) nie jest składem funkcji, a raczej składem soczewki, a id nie jest funkcją tożsamościową, ale obiektywem tożsamości: {[40]]}

id = iso id id

Co oznacza, że jeśli skomponujemy nasze dwie soczewki, wynik powinien być nieodróżnialny od tego obiektywu tożsamości.

 73
Author: Gabriel Gonzalez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-29 20:13:47

An izomorfizm u :: a -> b jest funkcją, która ma odwrotność, tj. inną funkcję v :: b -> a taką, że relacje

u . v = id
v . u = id

Są zadowoleni. Mówisz, że dwa typy są izomorficzne jeśli istnieje między nimi izomorfizm. Oznacza to zasadniczo, że możesz uznać je za ten sam typ - wszystko, co możesz zrobić z jednym, możesz zrobić z drugim.

Izomorfizm funkcji

Dwie funkcje typy

(a,b) -> c
a -> b -> c

Są izomorficzne, ponieważ możemy napisać

u :: ((a,b) -> c) -> a -> b -> c
u f = \x y -> f (x,y)

v :: (a -> b -> c) -> (a,b) -> c
v g = \(x,y) -> g x y

Możesz sprawdzić, czy u . v i v . u są obie id. W rzeczywistości funkcje u i {[16] } są lepiej znane pod nazwami curry i uncurry.

Izomorfizm i nowe typy

Wykorzystujemy izomorfizm, gdy używamy deklaracji newtype. Na przykład podstawowym typem monady stanu jest s -> (a,s), co może być nieco mylące. Używając nowego typu "deklaracja": {]}

newtype State s a = State { runState :: s -> (a,s) }

Generujemy nowy typ State s a, który jest izomorficzny z {[19] } i który daje do zrozumienia, że kiedy go używamy, myślimy o funkcjach, które mają stan modyfikowalny. Otrzymujemy również wygodny konstruktor State i getter runState dla nowego typu.

Monady i Komonady

Dla bardziej zaawansowanego punktu widzenia, rozważ izomorfizm za pomocą curry i uncurry, które użyłem powyżej. Typ Reader r a posiada deklarację newtype

newType Reader r a = Reader { runReader :: r -> a }

W w zależności od kontekstu monad, funkcja f produkująca czytnik ma więc sygnaturę typu

f :: a -> Reader r b

Co jest równoważne

f :: a -> r -> b

Który jest połową izomorfizmu curry ' ego / nieuregulowanego. Możemy również zdefiniować typ CoReader r a:

newtype CoReader r a = CoReader { runCoReader :: (a,r) }

Które można przekształcić w comonad. Istnieje funkcja cobind, czyli =>>, która przyjmuje funkcję, która pobiera coreader i tworzy surowy Typ:

g :: CoReader r a -> b

Która jest izomorficzna do

g :: (a,r) -> b

Ale już widzieliśmy, że a -> r -> b i (a,r) -> b są izomorficzne, co daje nam nietrywialny fakt: monada czytnika (z monadycznym wiązaniem) i comonada rdzenia (z comonadycznym cobindem) są również izomorficzne! W szczególności, oba mogą być używane do tego samego celu - czyli do zapewnienia globalnego środowiska, które jest gwintowane przez każde wywołanie funkcji.

 25
Author: Chris Taylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-29 07:49:29

Myśl w kategoriach typów danych. Na przykład w Haskell można myśleć o dwóch typach danych, które są izomorficzne, jeśli istnieje para funkcji, które przekształcają dane między nimi w unikalny sposób. Następujące trzy typy są względem siebie izomorficzne:

data Type1 a = Ax | Ay a
data Type2 a = Blah a | Blubb
data Maybe a = Just a | Nothing
Funkcje, które przekształcają się między nimi, można traktować jako izomorfizmy. To pasuje do kategorycznej idei izomorfizmu. Jeśli pomiędzy Type1 i Type2 istnieją dwie funkcje f i g z f . g = g . f = id, to dwie funkcje są izomorfizmami pomiędzy tymi dwoma typami (obiektami).
 13
Author: ertes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-28 14:16:00