Apa arti "coalgebra" dalam konteks pemrograman?

339

Saya telah mendengar istilah "coalgebras" beberapa kali dalam pemrograman fungsional dan lingkaran PLT, terutama ketika diskusi tentang objek, comonad, lensa, dan semacamnya. Googling istilah ini memberikan halaman-halaman yang memberikan deskripsi matematis dari struktur-struktur ini yang sangat tidak bisa saya pahami. Adakah yang bisa menjelaskan apa arti coalgebras dalam konteks pemrograman, apa signifikansinya, dan bagaimana kaitannya dengan objek dan comonad?

missingfaktor
sumber
21
Mungkinkah saya merekomendasikan buku Pola Jeremy Gibbons yang sangat baik di FP: polainfp.wordpress.com dan makalahnya yang cukup komprehensif "Menghitung Program Fungsional"? Mereka berdua menutupi batu bara batubara dengan cara yang sangat ketat (dibandingkan dengan, misalnya, posting blog), tetapi mereka juga cukup mandiri bagi seseorang yang tahu sedikit tentang Haskell.
Kristopher Micinski
2
@KristopherMicinski, sangat menarik. Terima kasih!
missingfaktor

Jawaban:

474

Aljabar

Saya pikir tempat untuk memulai adalah untuk memahami ide aljabar . Ini hanyalah generalisasi dari struktur aljabar seperti grup, cincin, monoids dan sebagainya. Sebagian besar waktu, hal-hal ini diperkenalkan dalam hal set, tetapi karena kita berada di antara teman-teman, saya akan berbicara tentang tipe Haskell sebagai gantinya. (Tapi aku tidak bisa menolak menggunakan beberapa huruf Yunani — mereka membuat semuanya terlihat lebih keren!)

Aljabar, kemudian, hanyalah tipe τdengan beberapa fungsi dan identitas. Fungsi-fungsi ini mengambil jumlah argumen yang berbeda dari jenis τdan menghasilkan τ: tidak terburu-buru, semuanya terlihat seperti (τ, τ,…, τ) → τ. Mereka juga dapat memiliki "identitas" - elemen τyang memiliki perilaku khusus dengan beberapa fungsi.

Contoh paling sederhana dari ini adalah monoid. Monoid adalah jenis apa pun τdengan fungsi mappend ∷ (τ, τ) → τdan identitas mzero ∷ τ. Contoh lain termasuk hal-hal seperti kelompok (yang seperti monoids kecuali dengan invert ∷ τ → τfungsi tambahan ), cincin, kisi dan sebagainya.

Semua fungsi beroperasi τtetapi dapat memiliki fungsi yang berbeda. Kita bisa menuliskan ini sebagai τⁿ → τ, di mana τⁿpeta ke tuple n τ. Dengan cara ini, masuk akal untuk memikirkan identitas sebagai di τ⁰ → τmana τ⁰hanya tuple kosong (). Jadi kita sebenarnya dapat menyederhanakan ide aljabar sekarang: itu hanya beberapa jenis dengan beberapa fungsi di atasnya.

Aljabar hanyalah pola umum dalam matematika yang telah "difaktorkan", seperti yang kita lakukan dengan kode. Orang-orang memperhatikan bahwa banyak hal menarik — monoid, kelompok, kisi, dan sebagainya yang disebutkan di atas — semuanya mengikuti pola yang sama, sehingga mereka mencabutnya. Keuntungan melakukan ini sama dengan pemrograman: ia menciptakan bukti yang dapat digunakan kembali dan membuat jenis penalaran tertentu lebih mudah.

F-Algebras

Namun, kami belum cukup selesai dengan anjak piutang. Sejauh ini, kami memiliki banyak fungsi τⁿ → τ. Kita sebenarnya dapat melakukan trik yang rapi untuk menggabungkan semuanya menjadi satu fungsi. Secara khusus, mari kita lihat monoids: yang kita miliki mappend ∷ (τ, τ) → τdan mempty ∷ () → τ. Kita bisa mengubahnya menjadi fungsi tunggal menggunakan tipe penjumlahan— Either. Akan terlihat seperti ini:

op  Monoid τ  Either (τ, τ) ()  τ
op (Left (a, b)) = mappend (a, b)
op (Right ())    = mempty

Kami benar-benar dapat menggunakan transformasi ini berulang kali untuk menggabungkan semua yang τⁿ → τfungsi ke dalam satu, untuk setiap aljabar. (Bahkan, kita bisa melakukan ini untuk sejumlah fungsi a → τ, b → τdan seterusnya untuk apa pun a, b,… .)

Ini memungkinkan kita berbicara tentang aljabar sebagai tipe τdengan fungsi tunggal dari beberapa kekacauan Eithers ke satu τ. Untuk monoids, kekacauan ini adalah: Either (τ, τ) (); untuk kelompok (yang memiliki tambahan τ → τoperasi), itu: Either (Either (τ, τ) τ) (). Ini adalah tipe yang berbeda untuk setiap struktur yang berbeda. Jadi apa kesamaan semua tipe ini? Yang paling jelas adalah bahwa semuanya hanyalah jumlah produk — tipe data aljabar. Sebagai contoh, untuk monoids, kita bisa menciptakan jenis argumen monoid yang bekerja untuk setiap monoid τ:

data MonoidArgument τ = Mappend τ τ -- here τ τ is the same as (τ, τ)
                      | Mempty      -- here we can just leave the () out

Kita dapat melakukan hal yang sama untuk grup dan cincin dan kisi dan semua struktur lain yang mungkin.

Apa lagi yang spesial dari semua tipe ini? Ya mereka semua Functors! Misalnya:

instance Functor MonoidArgument where
  fmap f (Mappend τ τ) = Mappend (f τ) (f τ)
  fmap f Mempty        = Mempty

Jadi kita bisa menggeneralisasi ide aljabar kita lebih jauh lagi. Hanya beberapa tipe τdengan fungsi f τ → τuntuk beberapa functor f. Bahkan, kita bisa menuliskan ini sebagai typeclass:

class Functor f  Algebra f τ where
  op  f τ  τ

Ini sering disebut "aljabar-F" karena ditentukan oleh functor F. Jika kita dapat menerapkan sebagian kacamata ketik, kita dapat mendefinisikan sesuatu seperti class Monoid = Algebra MonoidArgument.

Coalgebras

Sekarang, semoga Anda memiliki pemahaman yang baik tentang apa itu aljabar dan bagaimana itu hanya generalisasi dari struktur aljabar normal. Jadi apa itu F-coalgebra? Nah, co menyiratkan bahwa itu adalah "ganda" dari aljabar — yaitu, kita mengambil aljabar dan membalik beberapa panah. Saya hanya melihat satu panah dalam definisi di atas, jadi saya akan membalikkan itu:

class Functor f  CoAlgebra f τ where
  coop  τ  f τ

Dan hanya itu! Sekarang, kesimpulan ini mungkin tampak sedikit kurang ajar (heh). Ini memberi tahu Anda apa itu coalgebra, tetapi tidak benar-benar memberikan wawasan tentang manfaatnya atau mengapa kami peduli. Saya akan membahasnya sedikit, setelah saya menemukan atau menghasilkan satu atau dua contoh yang baik: P.

Kelas dan Objek

Setelah membaca sedikit, saya pikir saya punya ide bagus tentang cara menggunakan coalgebras untuk mewakili kelas dan objek. Kami memiliki tipe Cyang berisi semua kemungkinan keadaan internal objek di kelas; kelas itu sendiri adalah suatu coalgebra Cyang menspesifikasikan metode dan properti objek.

Seperti yang ditunjukkan pada contoh aljabar, jika kita memiliki banyak fungsi seperti a → τdan b → τuntuk apa pun a, b,…, kita dapat menggabungkan semuanya menjadi satu fungsi menggunakan Either, tipe penjumlahan. "Gagasan" ganda akan menggabungkan banyak fungsi tipe τ → a, τ → bdan seterusnya. Kita bisa melakukan ini menggunakan dual tipe penjumlahan — tipe produk. Jadi mengingat dua fungsi di atas (dipanggil fdan g), kita dapat membuat satu seperti:

both  τ  (a, b)
both x = (f x, g x)

Tipe (a, a)ini adalah functor dengan cara yang langsung, jadi tentu saja sesuai dengan gagasan kami tentang F-coalgebra. Trik khusus ini memungkinkan kita mengemas sekelompok fungsi yang berbeda — atau, untuk OOP, metode — menjadi satu fungsi tipe τ → f τ.

Elemen-elemen dari tipe kami Cmewakili keadaan internal objek. Jika objek memiliki beberapa properti yang dapat dibaca, mereka harus dapat bergantung pada keadaan. Cara yang paling jelas untuk melakukan ini adalah membuat mereka berfungsi C. Jadi jika kita menginginkan properti panjang (misalnya object.length), kita akan memiliki fungsi C → Int.

Kami ingin metode yang dapat mengambil argumen dan memodifikasi keadaan. Untuk melakukan ini, kita perlu mengambil semua argumen dan menghasilkan yang baru C. Mari kita bayangkan sebuah setPositionmetode yang mengambil sebuah xdan ykoordinat: object.setPosition(1, 2). Ini akan terlihat seperti ini: C → ((Int, Int) → C).

Pola penting di sini adalah bahwa "metode" dan "properti" dari objek mengambil objek itu sendiri sebagai argumen pertama mereka. Ini seperti selfparameter dalam Python dan seperti implisit thisbanyak bahasa lainnya. Sebuah coalgebra dasarnya hanya merangkum perilaku mengambil selfparameter: itulah yang pertama Cdi C → F Cadalah.

Jadi mari kita kumpulkan semuanya. Mari kita bayangkan kelas dengan positionproperti, properti, namedan setPositionfungsi:

class C
  private
    x, y  : Int
    _name : String
  public
    name        : String
    position    : (Int, Int)
    setPosition : (Int, Int)  C

Kami membutuhkan dua bagian untuk mewakili kelas ini. Pertama, kita perlu mewakili keadaan internal objek; dalam hal ini hanya memiliki dua Ints dan a String. (Ini adalah tipe kita C.) Maka kita perlu membuat bilangan batu bara yang mewakili kelas.

data C = Obj { x, y   Int
             , _name  String }

Kami memiliki dua properti untuk ditulis. Mereka cukup sepele:

position  C  (Int, Int)
position self = (x self, y self)

name  C  String
name self = _name self

Sekarang kita hanya perlu dapat memperbarui posisi:

setPosition  C  (Int, Int)  C
setPosition self (newX, newY) = self { x = newX, y = newY }

Ini seperti kelas Python dengan selfvariabel eksplisitnya . Sekarang kita memiliki banyak self →fungsi, kita harus menggabungkannya menjadi satu fungsi untuk batubara. Kita bisa melakukan ini dengan tuple sederhana:

coop  C  ((Int, Int), String, (Int, Int)  C)
coop self = (position self, name self, setPosition self)

Jenis ((Int, Int), String, (Int, Int) → c)-untuk setiap c -adalah functor, sehingga coopmemang memiliki bentuk yang kita inginkan: Functor f ⇒ C → f C.

Mengingat hal ini, Cbersama dengan coopmembentuk sebuah coalgebra yang menentukan kelas yang saya berikan di atas. Anda dapat melihat bagaimana kita dapat menggunakan teknik yang sama ini untuk menentukan sejumlah metode dan properti yang dimiliki objek kita.

Ini memungkinkan kami menggunakan alasan coalgebraic untuk berurusan dengan kelas. Sebagai contoh, kita dapat membawa gagasan tentang "homomorfisme F-coalgebra" untuk mewakili transformasi antar kelas. Ini adalah istilah yang menakutkan yang hanya berarti transformasi antara batubara batubara yang mempertahankan struktur. Ini membuatnya lebih mudah untuk berpikir tentang memetakan kelas ke kelas lain.

Singkatnya, F-coalgebra mewakili kelas dengan memiliki banyak properti dan metode yang semuanya bergantung pada selfparameter yang berisi keadaan internal masing-masing objek.

Kategori Lainnya

Sejauh ini, kita telah berbicara tentang algebras dan coalgebras sebagai tipe Haskell. Aljabar hanya tipe τdengan fungsi f τ → τdan aljabar hanya tipe τdengan fungsi τ → f τ.

Namun, tidak ada yang benar-benar mengikat ide-ide untuk Haskell per se . Bahkan, mereka biasanya diperkenalkan dalam hal set dan fungsi matematika daripada tipe dan fungsi Haskell. Memang, kita dapat menggeneralisasi konsep-konsep ini ke kategori apa pun !

Kita dapat mendefinisikan aljabar F untuk beberapa kategori C. Pertama, kita membutuhkan functor F : C → C—yaitu, endofunctor . (Semua Haskell Functorsebenarnya adalah endofunctors dari Hask → Hask.) Kemudian, aljabar hanyalah objek Adari Cdengan morfisme F A → A. Sebuah batubara adalah sama kecuali dengan A → F A.

Apa yang kita dapatkan dengan mempertimbangkan kategori lain? Kita dapat menggunakan ide yang sama dalam konteks yang berbeda. Seperti monad. Di Haskell, monad adalah beberapa tipe M ∷ ★ → ★dengan tiga operasi:

map         β)  (M α  M β)
return    α  M α
join      M (M α)  M α

The mapFungsi hanya bukti fakta bahwa Madalah Functor. Jadi kita dapat mengatakan bahwa monad hanya berfungsi dengan dua operasi: returndan join.

Functors membentuk kategori sendiri, dengan morfisme di antara mereka yang disebut "transformasi alami". Transformasi alami hanyalah cara untuk mengubah satu fungsi menjadi yang lain sambil mempertahankan strukturnya. Inilah artikel yang bagus membantu menjelaskan ide itu. Itu berbicara tentang concat, yang hanya joinuntuk daftar.

Dengan Haskell functors, komposisi dua functors adalah functor itu sendiri. Dalam pseudocode, kita bisa menulis ini:

instance (Functor f, Functor g)  Functor (f  g) where
  fmap fun x = fmap (fmap fun) x

Ini membantu kita berpikir joinsebagai pemetaan dari f ∘ f → f. Jenisnya joinadalah ∀α. f (f α) → f α. Secara intuitif, kita dapat melihat bagaimana suatu fungsi valid untuk semua tipe αdapat dianggap sebagai suatu transformasi dari f.

returnadalah transformasi serupa. Jenisnya adalah ∀α. α → f α. Ini terlihat berbeda — yang pertama αbukan "di" functor! Untungnya, kita bisa memperbaiki ini dengan menambahkan sebuah functor identitas sana: ∀α. Identity α → f α. Begitu returnjuga transformasi Identity → f.

Sekarang kita dapat berpikir tentang monad hanya sebagai aljabar yang didasarkan pada beberapa fungsi fdengan operasi f ∘ f → fdan Identity → f. Bukankah ini terlihat familier? Ini sangat mirip dengan monoid, yang hanya beberapa tipe τdengan operasi τ × τ → τdan () → τ.

Jadi monad seperti monoid, kecuali alih-alih memiliki tipe, kita memiliki functor. Ini adalah aljabar yang sama, hanya dalam kategori yang berbeda. (Di sinilah frasa "Monad hanya monoid dalam kategori endofunctors" berasal sejauh yang saya tahu.)

Sekarang, kami memiliki dua operasi ini: f ∘ f → fdan Identity → f. Untuk mendapatkan coalgebra yang sesuai, kita cukup membalikkan panah. Ini memberi kami dua operasi baru: f → f ∘ fdan f → Identity. Kita bisa mengubahnya menjadi tipe Haskell dengan menambahkan variabel tipe seperti di atas, memberi kita ∀α. f α → f (f α)dan ∀α. f α → α. Ini terlihat seperti definisi comonad:

class Functor f  Comonad f where
  coreturn  f α  α
  cojoin    f α  f (f α)

Jadi comonad kemudian menjadi sebuah coalgebra dalam kategori endofunctors.

Tikhon Jelvis
sumber
45
Ini sangat berharga. Saya berhasil mengaburkan beberapa intuisi tentang seluruh bisnis aljabar F ini dari membaca dan contoh (misalnya, dari melihat penggunaannya dengan katamoprhisme), tetapi ini semua sangat jelas bahkan bagi saya. Terima kasih!
Luis Casillas
28
Ini penjelasan yang bagus.
Edward KMETT
5
@ EdwardKmett: Terima kasih. Apakah barang yang saya tambahkan tentang kelas dan objek tidak apa-apa? Saya hanya membacanya hari ini, tetapi sepertinya masuk akal.
Tikhon Jelvis
7
Untuk apa nilainya: "Kategori endofunctors" di sini lebih tepatnya kategori yang objeknya adalah endofunctors pada beberapa kategori dan yang panahnya adalah transformasi alami. Ini adalah kategori monoid, dengan komposisi functor yang sesuai dengan (,)dan functor identitas (). Objek monoid dalam kategori monoid adalah objek dengan panah yang sesuai dengan aljabar monoid Anda, yang menggambarkan objek monoid dalam Hask dengan jenis produk sebagai struktur monoid. Objek monoid dalam kategori endofunctors pada C adalah monad pada C, jadi ya, pemahaman Anda benar. :]
CA McCann
8
Itu adalah akhir epik!
jdinunzio
85

F-algebras dan F-coalgebras adalah struktur matematika yang berperan dalam penalaran tentang tipe induktif (atau tipe rekursif ).

F-aljabar

Kami akan mulai dulu dengan F-algebras. Saya akan berusaha sesederhana mungkin.

Saya kira Anda tahu apa itu tipe rekursif. Misalnya, ini adalah tipe untuk daftar bilangan bulat:

data IntList = Nil | Cons (Int, IntList)

Jelas bahwa itu adalah rekursif - memang, definisi itu merujuk pada dirinya sendiri. Definisinya terdiri dari dua konstruktor data, yang memiliki tipe berikut:

Nil  :: () -> IntList
Cons :: (Int, IntList) -> IntList

Perhatikan bahwa saya telah menulis tipe Nilas () -> IntList, bukan hanya IntList. Ini sebenarnya adalah tipe yang setara dari sudut pandang teoretis, karena ()tipe hanya memiliki satu penduduk.

Jika kita menulis tanda tangan dari fungsi-fungsi ini dengan cara yang lebih teoritis, kita akan mendapatkannya

Nil  :: 1 -> IntList
Cons :: Int × IntList -> IntList

di mana 1satu set unit (set dengan satu elemen) dan A × Boperasi adalah produk silang dari dua set Adan B(yaitu, set pasangan (a, b)mana amelewati semua elemen Adan bmelewati semua elemen dari B).

Disjoint penyatuan dua set Adan Bmerupakan set A | Byang merupakan penyatuan set {(a, 1) : a in A}dan {(b, 2) : b in B}. Pada dasarnya itu adalah satu set semua elemen dari keduanya Adan B, tetapi dengan masing-masing elemen ini 'ditandai' sebagai milik salah satu Aatau B, jadi ketika kita mengambil elemen dari A | Bkita akan segera tahu apakah elemen ini berasal Aatau dari B.

Kami dapat 'bergabung' Nildan Consfungsi, sehingga mereka akan membentuk satu fungsi yang bekerja pada set 1 | (Int × IntList):

Nil|Cons :: 1 | (Int × IntList) -> IntList

Memang, jika Nil|Consfungsi diterapkan pada ()nilai (yang, jelas, milik 1 | (Int × IntList)set), maka berperilaku seolah-olah itu Nil; jika Nil|Consditerapkan pada nilai tipe apa pun (Int, IntList)(nilai tersebut juga ada di set 1 | (Int × IntList), itu berlaku sebagai Cons.

Sekarang pertimbangkan tipe data lain:

data IntTree = Leaf Int | Branch (IntTree, IntTree)

Ini memiliki konstruktor berikut:

Leaf   :: Int -> IntTree
Branch :: (IntTree, IntTree) -> IntTree

yang juga bisa digabung menjadi satu fungsi:

Leaf|Branch :: Int | (IntTree × IntTree) -> IntTree

Dapat dilihat bahwa kedua joinedfungsi ini memiliki tipe yang sama: keduanya terlihat seperti

f :: F T -> T

di mana Fadalah jenis transformasi yang mengambil tipe kita dan memberikan tipe yang lebih kompleks, yang terdiri dari xdan |operasi, penggunaan Tdan mungkin tipe lainnya. Misalnya, untuk IntListdan IntTree Fterlihat sebagai berikut:

F1 T = 1 | (Int × T)
F2 T = Int | (T × T)

Kita dapat segera melihat bahwa semua jenis aljabar dapat ditulis dengan cara ini. Memang, itulah sebabnya mereka disebut 'aljabar': mereka terdiri dari sejumlah 'jumlah' (serikat pekerja) dan 'produk' (produk silang) dari jenis lain.

Sekarang kita dapat mendefinisikan aljabar F. Aljabar F hanyalah sepasang (T, f), di mana Tada beberapa jenis dan fmerupakan fungsi dari jenis f :: F T -> T. Dalam contoh-contoh kami F-aljabar adalah (IntList, Nil|Cons)dan (IntTree, Leaf|Branch). Namun, perlu diketahui bahwa meskipun jenis ffungsinya sama untuk masing-masing F, Tdan fdirinya sendiri dapat arbitrer. Misalnya, (String, g :: 1 | (Int x String) -> String)atau (Double, h :: Int | (Double, Double) -> Double)untuk beberapa gdan hjuga F-aljabar untuk F. yang sesuai

Setelah itu kita dapat memperkenalkan homomorfisme aljabar F-aljabar dan kemudian memulai aljabar F-aljabar , yang memiliki sifat yang sangat berguna. Sebenarnya, (IntList, Nil|Cons)adalah aljabar F1 awal, dan (IntTree, Leaf|Branch)merupakan aljabar F2 awal. Saya tidak akan menyajikan definisi yang tepat dari istilah dan properti ini karena mereka lebih kompleks dan abstrak daripada yang dibutuhkan.

Meskipun demikian, fakta bahwa, katakanlah, (IntList, Nil|Cons)aljabar-F memungkinkan kita untuk mendefinisikan foldfungsi seperti pada tipe ini. Seperti yang Anda ketahui, fold adalah semacam operasi yang mengubah beberapa tipe data rekursif dalam satu nilai terbatas. Misalnya, kita bisa melipat daftar bilangan bulat menjadi nilai tunggal yang merupakan jumlah semua elemen dalam daftar:

foldr (+) 0 [1, 2, 3, 4] -> 1 + 2 + 3 + 4 = 10

Dimungkinkan untuk menggeneralisasi operasi semacam itu pada setiap tipe data rekursif.

Berikut ini adalah tanda tangan foldrfungsi:

foldr :: ((a -> b -> b), b) -> [a] -> b

Perhatikan bahwa saya telah menggunakan kawat gigi untuk memisahkan dua argumen pertama dari yang terakhir. Ini bukan foldrfungsi nyata , tetapi isomorfis dengannya (yaitu, Anda dapat dengan mudah mendapatkan satu dari yang lain dan sebaliknya). Sebagian diterapkan foldrakan memiliki tanda tangan berikut:

foldr ((+), 0) :: [Int] -> Int

Kita dapat melihat bahwa ini adalah fungsi yang mengambil daftar bilangan bulat dan mengembalikan bilangan bulat tunggal. Mari kita definisikan fungsi seperti itu dalam hal IntListtipe kita .

sumFold :: IntList -> Int
sumFold Nil         = 0
sumFold (Cons x xs) = x + sumFold xs

Kita melihat bahwa fungsi ini terdiri dari dua bagian: bagian pertama mendefinisikan perilaku fungsi ini pada Nilbagian dari IntList, dan bagian kedua mendefinisikan perilaku fungsi pada Consbagian.

Sekarang anggaplah kita memprogram bukan dalam Haskell tetapi dalam beberapa bahasa yang memungkinkan penggunaan tipe-tipe aljabar secara langsung dalam tipe tanda tangan (yah, secara teknis Haskell memungkinkan penggunaan tipe-tipe aljabar melalui tupel dan Either a btipe data, tetapi ini akan mengarah pada verbositas yang tidak perlu). Pertimbangkan fungsi:

reductor :: () | (Int × Int) -> Int
reductor ()     = 0
reductor (x, s) = x + s

Dapat dilihat bahwa reductorini adalah fungsi dari tipe F1 Int -> Int, seperti dalam definisi aljabar F! Memang, pasangan (Int, reductor)adalah aljabar F1.

Karena IntListmerupakan F1-aljabar awal, untuk setiap jenis Tdan untuk setiap fungsi r :: F1 T -> Tterdapat fungsi, yang disebut catamorphism untuk r, yang bertobat IntListuntuk T, dan fungsi tersebut adalah unik. Memang, dalam contoh kita, katamorfisme reductoradalah sumFold. Perhatikan bagaimana reductordan sumFoldmirip: mereka memiliki struktur yang hampir sama! Dalam reductordefinisi sparameter, penggunaan (tipe yang sesuai dengan T) sesuai dengan penggunaan hasil perhitungan sumFold xsdalam sumFolddefinisi.

Hanya untuk membuatnya lebih jelas dan membantu Anda melihat polanya, berikut adalah contoh lain, dan kita kembali mulai dari fungsi lipat yang dihasilkan. Pertimbangkan appendfungsi yang menambahkan argumen pertama ke argumen kedua:

(append [4, 5, 6]) [1, 2, 3] = (foldr (:) [4, 5, 6]) [1, 2, 3] -> [1, 2, 3, 4, 5, 6]

Ini tampilannya pada kita IntList:

appendFold :: IntList -> IntList -> IntList
appendFold ys ()          = ys
appendFold ys (Cons x xs) = x : appendFold ys xs

Sekali lagi, mari kita coba menuliskan reduktor:

appendReductor :: IntList -> () | (Int × IntList) -> IntList
appendReductor ys ()      = ys
appendReductor ys (x, rs) = x : rs

appendFoldadalah katamorfisme appendReductoryang ditransformasikan IntListmenjadi IntList.

Jadi, pada dasarnya, F-algebras memungkinkan kita untuk mendefinisikan 'lipatan' pada struktur data rekursif, yaitu operasi yang mengurangi struktur kita ke beberapa nilai.

F-coalgebras

F-coalgebras adalah istilah yang disebut 'ganda' untuk F-algebras. Mereka memungkinkan kita untuk menentukan unfoldstipe data rekursif, yaitu cara untuk membangun struktur rekursif dari beberapa nilai.

Misalkan Anda memiliki tipe berikut:

data IntStream = Cons (Int, IntStream)

Ini adalah aliran bilangan bulat tanpa batas. Satu-satunya konstruktor memiliki jenis berikut:

Cons :: (Int, IntStream) -> IntStream

Atau, dalam hal set

Cons :: Int × IntStream -> IntStream

Haskell memungkinkan Anda untuk mencocokkan pola pada konstruktor data, sehingga Anda dapat menentukan fungsi berikut yang bekerja pada IntStreams:

head :: IntStream -> Int
head (Cons (x, xs)) = x

tail :: IntStream -> IntStream
tail (Cons (x, xs)) = xs

Anda secara alami dapat 'menggabungkan' fungsi-fungsi ini menjadi satu fungsi tipe IntStream -> Int × IntStream:

head&tail :: IntStream -> Int × IntStream
head&tail (Cons (x, xs)) = (x, xs)

Perhatikan bagaimana hasil fungsi bertepatan dengan representasi aljabar dari IntStreamtipe kita . Hal serupa juga dapat dilakukan untuk tipe data rekursif lainnya. Mungkin Anda sudah memperhatikan polanya. Saya mengacu pada keluarga fungsi tipe

g :: T -> F T

dimana Tbeberapa tipe. Mulai sekarang kita akan mendefinisikan

F1 T = Int × T

Sekarang, F-coalgebra adalah pasangan (T, g), di mana Tadalah tipe dan gmerupakan fungsi dari tipe g :: T -> F T. Sebagai contoh, (IntStream, head&tail)adalah F1-coalgebra. Sekali lagi, seperti dalam F-algebras, gdan Tdapat berubah-ubah, misalnya, (String, h :: String -> Int x String)juga merupakan F1-coalgebra untuk beberapa jam.

Di antara semua F-coalgebras ada yang disebut terminal F-coalgebras , yang merupakan dwi-aljabar F-coal. Sebagai contoh, IntStreamadalah terminal F-coalgebra. Ini berarti bahwa untuk setiap jenis Tdan untuk setiap fungsi p :: T -> F1 Tterdapat fungsi, yang disebut anamorphism , yang dikonversi Tmenjadi IntStream, dan fungsi tersebut adalah unik.

Pertimbangkan fungsi berikut, yang menghasilkan aliran bilangan bulat berturut-turut mulai dari yang diberikan:

nats :: Int -> IntStream
nats n = Cons (n, nats (n+1))

Sekarang mari kita periksa fungsi natsBuilder :: Int -> F1 Int, yaitu natsBuilder :: Int -> Int × Int:

natsBuilder :: Int -> Int × Int
natsBuilder n = (n, n+1)

Sekali lagi, kita dapat melihat beberapa kesamaan antara natsdan natsBuilder. Ini sangat mirip dengan koneksi yang telah kami amati dengan reduktor dan lipatan sebelumnya. natsadalah anamorphism untuk natsBuilder.

Contoh lain, fungsi yang mengambil nilai dan fungsi dan mengembalikan aliran aplikasi berturut-turut fungsi ke nilai:

iterate :: (Int -> Int) -> Int -> IntStream
iterate f n = Cons (n, iterate f (f n))

Fungsi pembangunnya adalah sebagai berikut:

iterateBuilder :: (Int -> Int) -> Int -> Int × Int
iterateBuilder f n = (n, f n)

Maka iteratemerupakan anamorphism untuk iterateBuilder.

Kesimpulan

Jadi, singkatnya, F-aljabar memungkinkan untuk menentukan lipatan, yaitu operasi yang mengurangi struktur rekursif menjadi nilai tunggal, dan F-coalgebra memungkinkan untuk melakukan yang sebaliknya: membangun struktur [berpotensi] tak terbatas dari nilai tunggal.

Faktanya dalam Haskell F-algebras dan F-coalgebras bertepatan. Ini adalah properti yang sangat bagus yang merupakan konsekuensi dari keberadaan nilai 'bawah' di setiap jenis. Jadi di Haskell lipatan dan lipatan dapat dibuat untuk setiap jenis rekursif. Namun, model teoritis di balik ini lebih kompleks daripada yang saya sebutkan di atas, jadi saya sengaja menghindarinya.

Semoga ini membantu.

Vladimir Matveev
sumber
Jenis dan definisi appendReductorterlihat agak aneh dan tidak benar-benar membantu saya melihat polanya di sana ... :) Bisakah Anda mengecek apakah itu benar? .. Seperti apa jenis reduktor pada umumnya? Dalam definisi rsana, F1ditentukan oleh IntList, atau apakah itu F sewenang-wenang?
Max Galkin
37

Menelusuri makalah tutorial Sebuah tutorial tentang (co) aljabar dan (co) induksi harus memberi Anda beberapa wawasan tentang co-aljabar dalam ilmu komputer.

Di bawah ini adalah kutipan untuk meyakinkan Anda,

Secara umum, suatu program dalam beberapa bahasa pemrograman memanipulasi data. Selama pengembangan ilmu komputer selama beberapa dekade terakhir, menjadi jelas bahwa deskripsi abstrak dari data ini diinginkan, misalnya untuk memastikan bahwa program seseorang tidak tergantung pada representasi khusus dari data yang digunakannya. Juga, keabstrakan tersebut memfasilitasi bukti kebenaran.
Keinginan ini menyebabkan penggunaan metode aljabar dalam ilmu komputer, di cabang yang disebut spesifikasi aljabar atau teori tipe data abstrak. Objek penelitian adalah tipe data dalam diri mereka sendiri, menggunakan gagasan teknik yang akrab dari aljabar. Tipe data yang digunakan oleh para ilmuwan komputer sering dihasilkan dari kumpulan operasi (konstruktor) tertentu, dan untuk alasan inilah "inisialisasi" aljabar memainkan peran yang sangat penting.
Teknik aljabar standar telah terbukti berguna dalam menangkap berbagai aspek penting dari struktur data yang digunakan dalam ilmu komputer. Tetapi ternyata sulit untuk menggambarkan secara aljabar beberapa struktur yang secara inheren dinamis terjadi dalam komputasi. Struktur seperti itu biasanya melibatkan gagasan tentang negara, yang dapat diubah dengan berbagai cara. Pendekatan formal untuk sistem dinamis berbasis negara seperti itu umumnya menggunakan automata atau sistem transisi, sebagai referensi awal klasik.
Selama dekade terakhir wawasan secara bertahap tumbuh bahwa sistem berbasis negara seperti itu tidak boleh digambarkan sebagai aljabar, tetapi sebagai apa yang disebut co-aljabar. Ini adalah dual formal aljabar, dengan cara yang akan dibuat tepat dalam tutorial ini. Sifat ganda dari "inisialisasi" untuk aljabar, yaitu finalitas ternyata sangat penting untuk co-aljabar tersebut. Dan prinsip penalaran logis yang diperlukan untuk co-aljabar akhir seperti itu bukanlah induksi melainkan co-induksi.


Pendahuluan, tentang teori Kategori. Kategori teori harus berganti nama menjadi teori functors. Karena kategori adalah apa yang harus didefinisikan seseorang untuk mendefinisikan functors. (Selain itu, functors adalah apa yang harus didefinisikan untuk mendefinisikan transformasi alami.)

Apa itu functor? Ini adalah transformasi dari satu set ke set lainnya yang menjaga struktur mereka. (Untuk lebih jelasnya ada banyak deskripsi yang bagus di internet).

Apa itu aljabar F? Ini adalah aljabar functor. Ini hanya studi tentang kepatutan universal functor.

Bagaimana itu bisa terhubung ke ilmu komputer? Program dapat dilihat sebagai kumpulan informasi terstruktur. Eksekusi program berhubungan dengan modifikasi set informasi terstruktur ini. Kedengarannya bagus bahwa eksekusi harus mempertahankan struktur program. Kemudian eksekusi dapat dilihat sebagai aplikasi functor atas set informasi ini. (Yang mendefinisikan program).

Mengapa F-co-aljabar? Program bersifat ganda karena pada dasarnya dijelaskan oleh informasi dan mereka bertindak berdasarkan itu. Maka terutama informasi yang menyusun program dan mengubahnya dapat dilihat dengan dua cara.

  • Data yang dapat didefinisikan sebagai informasi yang sedang diproses oleh program.
  • Negara yang dapat didefinisikan sebagai informasi yang dibagikan oleh program.

Kemudian pada tahap ini, saya ingin mengatakan itu,

  • Aljabar F adalah studi tentang transformasi fungsi yang bertindak atas Data's Universe (seperti yang didefinisikan di sini).
  • F-co-algebras adalah studi transformasi functorial yang bekerja pada State's Universe (sebagaimana telah didefinisikan di sini).

Selama kehidupan suatu program, data dan negara berdampingan, dan mereka saling melengkapi. Mereka ganda.

zurgl
sumber
5

Saya akan mulai dengan hal-hal yang jelas terkait dengan pemrograman dan kemudian menambahkan beberapa hal matematika, agar tetap konkrit dan serendah mungkin.


Mari mengutip beberapa ilmuwan komputer tentang koinduksi ...

http://www.cs.umd.edu/~micinski/posts/2012-09-04-on-understanding-coinduction.html

Induksi adalah tentang data hingga, co-induksi adalah tentang data tak terbatas.

Contoh khas dari data tak terbatas adalah jenis daftar malas (stream). Misalnya, katakanlah kita memiliki objek berikut dalam memori:

 let (pi : int list) = (* some function which computes the digits of
 π. *)

Komputer tidak dapat menampung semua π, karena hanya memiliki jumlah memori terbatas! Tapi apa yang bisa dilakukan adalah mengadakan program terbatas, yang akan menghasilkan ekspansi long yang Anda inginkan. Selama Anda hanya menggunakan potongan-potongan daftar yang terbatas, Anda dapat menghitung dengan daftar yang tak terbatas sebanyak yang Anda butuhkan.

Namun, pertimbangkan program berikut:

let print_third_element (k : int list) =   match k with
     | _ :: _ :: thd :: tl -> print thd


 print_third_element pi

Program ini harus mencetak digit pi ketiga. Tetapi dalam beberapa bahasa, setiap argumen terhadap suatu fungsi dievaluasi sebelum diteruskan ke suatu fungsi (evaluasi yang ketat, tidak malas). Jika kami menggunakan urutan pengurangan ini, maka program kami di atas akan berjalan selamanya menghitung digit pi sebelum dapat diteruskan ke fungsi printer kami (yang tidak pernah terjadi). Karena mesin tidak memiliki memori tak terbatas, program pada akhirnya akan kehabisan memori dan crash. Ini mungkin bukan urutan evaluasi terbaik.

http://adam.chlipala.net/cpdt/html/Coinductive.html

Dalam bahasa pemrograman fungsional yang malas seperti Haskell, struktur data tak terbatas ada di mana-mana. Daftar yang tidak terbatas dan tipe data yang lebih eksotis memberikan abstraksi yang nyaman untuk komunikasi antara bagian-bagian dari suatu program. Mencapai kenyamanan serupa tanpa struktur malas yang tak terbatas akan, dalam banyak kasus, memerlukan inversi akrobatik dari aliran kontrol.

http://www.alexandrasilva.org/#/talks.html contoh coalgebras oleh Alexandra Silva


Menghubungkan konteks matematika ambient dengan tugas pemrograman biasa

Apa itu "aljabar"?

Struktur aljabar umumnya terlihat seperti:

  1. Barang
  2. Apa yang bisa dilakukan barang

Ini seharusnya terdengar seperti objek dengan 1. properti dan 2. metode. Atau bahkan lebih baik, seharusnya terdengar seperti tanda tangan tipe.

Contoh matematika standar termasuk monoid ⊃ grup ⊃ vektor-ruang ⊃ "aljabar". Monoids seperti automata: urutan kata kerja (misalnya, f.g.h.h.nothing.f.g.f). Sebuah gitlog yang selalu menambahkan sejarah dan tidak pernah menghapus itu akan menjadi monoid tapi bukan kelompok. Jika Anda menambahkan invers (mis. Angka negatif, pecahan, akar, menghapus histori yang terakumulasi, menghapus pecahan cermin yang rusak) Anda mendapatkan grup.

Grup berisi hal-hal yang dapat ditambahkan atau dikurangkan bersama. Misalnya Durations dapat ditambahkan bersamaan. (Tapi Dates tidak bisa.) Durasi hidup dalam ruang vektor (bukan hanya grup) karena mereka juga dapat diskalakan dengan angka luar. (Jenis tanda tangan dari scaling :: (Number,Duration) → Duration.)

Algebras ⊂ ruang vektor dapat melakukan hal lain: ada beberapa m :: (T,T) → T. Sebut ini "perkalian" atau tidak, karena begitu Anda meninggalkannya Integers, menjadi tidak jelas apa "perkalian" (atau "eksponensial" ) seharusnya.

(Inilah sebabnya mengapa orang mencari sifat universal (kategori-teoretis): untuk memberi tahu mereka apa yang harus dilakukan atau menjadi seperti :

properti universal produk )


Algebras → Coalgebras

Komultiplikasi lebih mudah untuk didefinisikan dengan cara yang terasa non-arbitrer, daripada multiplikasi, karena untuk pergi dari T → (T,T)Anda hanya dapat mengulangi elemen yang sama. ("peta diagonal" - seperti matriks / operator diagonal dalam teori spektral)

Counit biasanya merupakan jejak (jumlah entri diagonal), meskipun lagi yang penting adalah apa yang dilakukan oleh counit Anda ; tracehanyalah jawaban yang bagus untuk matriks.

Alasan untuk melihat ruang ganda , secara umum, adalah karena lebih mudah untuk berpikir di ruang itu. Misalnya kadang-kadang lebih mudah untuk memikirkan vektor normal daripada tentang pesawat normal, tetapi Anda dapat mengontrol pesawat (termasuk hyperplanes) dengan vektor (dan sekarang saya berbicara tentang vektor geometris yang sudah dikenal, seperti di tracer-ray) .


Menjinakkan (tidak) data terstruktur

Matematikawan mungkin memodelkan sesuatu yang menyenangkan seperti TQFT , sedangkan programmer harus bergulat dengannya

  • tanggal / waktu ( + :: (Date,Duration) → Date),
  • tempat ( Paris(+48.8567,+2.3508)! Ini bentuk, bukan titik.),
  • JSON tidak terstruktur yang seharusnya konsisten dalam beberapa hal,
  • XML salah-tapi-tutup,
  • data GIS yang sangat kompleks yang harus memuaskan banyak hubungan yang masuk akal,
  • ekspresi reguler yang berarti bagi Anda, tetapi jauh lebih sedikit berarti bagi perl.
  • CRM yang harus menyimpan semua nomor telepon eksekutif dan lokasi villa, nama (dan mantan istri) anak-anaknya, ulang tahun dan semua hadiah sebelumnya, yang masing-masing harus memenuhi hubungan "jelas" (jelas bagi pelanggan) yang luar biasa sulit untuk dikodekan,
  • .....

Ilmuwan komputer, ketika berbicara tentang batu bara batubara, biasanya memiliki operasi tertentu, seperti produk Cartesian. Saya percaya inilah yang dimaksud orang ketika mereka berkata seperti "Algebras adalah coalgebras di Haskell". Tetapi sejauh para programmer harus memodelkan tipe data yang kompleks seperti Place,, Date/Timedan Customer— dan membuat model-model itu tampak sama seperti dunia nyata (atau setidaknya pandangan pengguna akhir tentang dunia nyata) sebanyak mungkin — saya percaya ada dua, bisa bermanfaat di luar hanya dunia set.

isomorfisma
sumber