Значення ізоморфних функцій


74

Коротке запитання: Яке значення ізоморфних функцій у програмуванні (а саме у функціональному програмуванні)?

Довге запитання: Я намагаюся зробити деякі аналоги між функціональним програмуванням та поняттями в теорії категорій, засновані на деяких мовах, які я час від часу чую. По суті, я намагаюся «розпакувати» цей жаргон у щось конкретне, що я можу потім розширити. Тоді я зможу вживати жаргону з розумінням того, про що я говорю. Що завжди приємно.

Одним із цих термінів, який я постійно чую, є Ізоморфізм , я вважаю, що мова йде про міркування про еквівалентність між функціями або їх складом. Мені було цікаво, чи може хтось дати уявлення про деякі загальні закономірності, коли властивість ізоморфізму стає в нагоді (при функціональному програмуванні), та будь-які побічні продукти, такі як оптимізація компілятора з міркувань про ізоморфні функції.


Це чудове питання. Ви створюєте їх серію? Якщо так, то вам слід десь зібрати посилання та зробити посилання на колекцію у своєму профілі.
Marcin

2
@Marcin Я справді, повідомлю вас, коли їх усі
зберу

3
Не лише чудове запитання, але й велике використання StackOverflow!
Jörg W Mittag

iso-кошти рівні (/ еквівалент): en.wiktionary.org/wiki/iso-#Etymology і -morph-засоби форма / форма: thefreedictionary.com/morph . Отже, ізоморфізм - це відношення / функція еквівалентності між двома типами, як це можна визначити, вже дуже добре пояснено нижче.
Cetin Sert

Відповіді:


81

Я беру невеличку проблему з високо оціненою відповіддю на ізоморфізм, оскільки визначення теорії категорій ізоморфізму нічого не говорить про об'єкти. Щоб зрозуміти чому, давайте переглянемо визначення.

Визначення

Ізоморфізм - це пара морфізмів (тобто функцій) fі gтака, що:

f . g = id
g . f = id

Потім ці морфізми називаються "ізо" морфізмами. Багато людей не розуміють, що "морфізм" в ізоморфізмі стосується функції, а не об'єкта. Однак ви б сказали, що об’єкти, які вони з’єднують, є «ізоморфними», що описує інша відповідь.

Зверніть увагу, що визначення ізоморфізму не говорить, що ( .) id, або =повинно бути. Єдина вимога полягає в тому, щоб, якими б вони не були, вони також відповідали законам категорії:

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

Композиція (тобто ( .)) поєднує два морфізми в один морфізм і idпозначає якийсь перехід "ідентичності". Це означає, що якщо наші ізоморфізми відмінюються до морфізму тотожності id, то ви можете сприймати їх як обернені один одного.

Для конкретного випадку, коли морфізми є функціями, тоді idвизначається як функція ідентичності:

id x = x

... і склад визначається як:

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

... і дві функції є ізоморфізмами, якщо вони idскладаються з функції тотожності під час їх складання.

Морфізми проти об’єктів

Однак існує кілька способів, як два об’єкти можуть бути ізоморфними. Наприклад, враховуючи такі два типи:

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

Між ними є два ізоморфізми:

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

Отож, тому краще описувати ізоморфізм з точки зору конкретних функцій, що стосуються двох об’єктів, а не двох об’єктів, оскільки між двома об’єктами може не бути унікальної пари функцій, які задовольняють законам ізоморфізму.

Також зверніть увагу, що недостатньо, щоб функції були оберненими. Наприклад, наступні пари функцій не є ізоморфізмами:

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

Незважаючи на те, що жодна інформація не втрачається під час написання f1 . g2, ви не повертаєтеся до початкового стану, навіть якщо остаточний стан має той самий тип.

Крім того, ізоморфізми не повинні знаходитись між конкретними типами даних. Ось приклад двох канонічних ізоморфізмів не між конкретними алгебраїчними типами даних, а натомість просто пов’язують функції: curryі uncurry:

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

Використання для ізоморфізмів

Церковне кодування

Одним із варіантів використання ізоморфізмів є кодування Церквою типів даних як функцій. Наприклад, Boolє ізоморфним для 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

Перевірте, що f . g = idі g . f = id.

Перевага церковного кодування типів даних полягає в тому, що вони іноді працюють швидше (оскільки церковне кодування є стилем, що передає продовження), і вони можуть бути реалізовані мовами, які взагалі не мають мовної підтримки алгебраїчних типів даних.

Переклад реалізацій

Іноді намагаються порівняти реалізацію однієї бібліотеки якоїсь функції з реалізацією іншої бібліотеки, і якщо ви можете довести, що вони ізоморфні, то ви можете довести, що вони однаково потужні. Крім того, ізоморфізми описують, як перекласти одну бібліотеку в іншу.

Наприклад, є два підходи, що забезпечують можливість визначення монади за підписом функтора. Один - це вільна монада, що надається freeпакетом, а інший - операційна семантика, передбачена operationalпакетом.

Якщо розглянути два основні типи даних, вони виглядають по-різному, особливо їх другі конструктори:

-- 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))

... але вони насправді ізоморфні! Це означає, що обидва підходи однаково потужні, і будь-який код, написаний в одному підході, можна механічно перевести в інший підхід, використовуючи ізоморфізми.

Ізоморфізми, які не є функціями

Крім того, ізоморфізми не обмежуються функціями. Вони фактично визначені для будь-якого, Categoryі Haskell має безліч категорій. Ось чому корисніше думати з точки зору морфізмів, а не типів даних.

Наприклад, Lensтип (від data-lens) утворює категорію, де ви можете складати лінзи та мати ідентичні лінзи. Отже, використовуючи наведений вище тип даних, ми можемо визначити дві лінзи, які є ізоморфізмами:

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

Зверніть увагу, що у грі є два ізоморфізми. Одним із них є ізоморфізм, який використовується для побудови кожної лінзи (тобто f1і g1) (і тому також називається ця конструктивна функція iso), і тоді самі лінзи також є ізоморфізмами. Зверніть увагу, що у наведеному вище формулюванні використана композиція ( .) - не функціональна композиція, а склад лінзи, і idне є функцією ідентичності, а натомість є ідентичною лінзою:

id = iso id id

Це означає, що якщо ми складемо наші дві лінзи, результат повинен бути невідмінним від цієї ідентичної лінзи.


Це допомогло б показати ізоморфізм між Programі Free; Я не можу це зробити з одного погляду.
Радіатор

Program fє ізоморфним Free (Coyoneda f)і Coyoneda fізоморфним f, тому два типи ізоморфні. Я це залишив поза увагою, бо доказ трохи більше пов’язаний.
Габріель Гонсалес,

Я хотів би зрозуміти, що відбувається з програмою. Це дійсне визначення в такому вигляді? Яка роль instr? Я припускаю, що це могло б стосуватися редагування заплутаних помилок, яке я був би радий бачити, але не можу зрозуміти, як це зробити сам.
свинарник

@pigworker Я скопіював це з operational, але видалив базову монаду, щоб зробити її звичайною монадою замість трансформатора монади. У процесі я забув видалити Ts з конструкторів, але виправив.
Габріель Гонсалес,

@GabrielGonzalez А якій меті служить instr?
свинарник

25

Ізоморфізм u :: a -> b є функцією , яка має зворотну , тобто іншу функцію v :: b -> a таким чином, що відносини

u . v = id
v . u = id

задоволені. Ви кажете, що два типи ізоморфні, якщо між ними існує ізоморфізм. Це по суті означає, що ви можете вважати їх однотипними - все, що ви можете зробити з одним, ви можете зробити з іншим.

Ізоморфізм функцій

Два типи функцій

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

є ізоморфними, оскільки ми можемо писати

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

Ви можете перевірити це u . vі v . uобидва id. Насправді функції uі vбільш відомі під іменами curryта uncurry.

Ізоморфізм та нові типи

Ми використовуємо ізоморфізм щоразу, коли використовуємо декларацію нового типу. Наприклад, основним типом державної монади є такий, про s -> (a,s)який може бути трохи заплутано думати. За допомогою оголошення newtype:

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

ми генеруємо новий тип, State s aякий є ізоморфним s -> (a,s)і який дає зрозуміти, коли ми його використовуємо, ми думаємо про функції, які можуть змінюватися. Ми також отримуємо зручний конструктор Stateта геттер runStateдля нового типу.

Монади та комони

Для більш просунутою точки зору, розглянемо ізоморфізм , використовуючи curryі uncurryщо я використовував вище. Reader r aТип має декларацію NewType

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

fОтже, в контексті монад функція, що виробляє зчитувач, має підпис типу

f :: a -> Reader r b

що еквівалентно

f :: a -> r -> b

що становить половину ізоморфізму каррі / uncurry. Ми також можемо визначити CoReader r aтип:

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

з якого можна зробити комонаду. Там ми маємо функцію cobind, або =>>яка приймає функцію, яка приймає coreader і створює необроблений тип:

g :: CoReader r a -> b

яка ізоморфна

g :: (a,r) -> b

Але ми вже бачили, що a -> r -> bі (a,r) -> bє ізоморфними, що дає нам нетривіальний факт: монада-читач (з монадичною прив’язкою) і комонад-комонада (з комонадичною співучастю) також ізоморфні! Зокрема, обидва вони можуть використовуватися для однієї і тієї ж мети - забезпечення глобального середовища, яке проходить через кожен виклик функції.


Хм? Я забув оператор композиції '.' .. що це знову? Конкатенація? .. noo

13

Подумайте з точки зору типів даних. Наприклад, у Haskell ви можете думати про два типи даних, які є ізоморфними, якщо існує пара функцій, які унікальним чином перетворюють дані між собою. Наступні три типи ізоморфні один одному:

data Type1 a = Ax | Ay a
data Type2 a = Blah a | Blubb
data Maybe a = Just a | Nothing

Ви можете сприймати функції, що перетворюються між ними, як ізоморфізми. Це відповідає категоричній ідеї ізоморфізму. Якщо між Type1і Type2існують дві функції fі gз f . g = g . f = id, тоді ці дві функції є ізоморфізмами між цими двома типами (об'єктами).


5
Тільки додамо: технічний термін для функцій типу fі gє бієкцією.
huon

2
З mapви також зможете побудувати ізоморфізм між різними типами списків; однакові для fmapі аплікативних типів.
Ріккардо Т.

2
Отже, типи даних ізоморфні, якщо між ними існує бієктивна залежність?
Marcin

@ertes, щоб ідентифікатор у наступному випадку був x? func = f . g...func x
ThaDon
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.