“Kömür” programlama bağlamında ne anlama geliyor?


339

Fonksiyonel programlama ve PLT çevrelerinde, özellikle tartışmalar nesneler, komonadlar, lensler ve benzerleriyle ilgili olduğunda, "kömürgebralar" terimini birkaç kez duydum. Bu terimi araştırmak, bu yapıların benim için neredeyse anlaşılmaz olan matematiksel açıklamasını veren sayfalar veriyor. Herhangi biri kömür bağlamında programlama bağlamında ne anlama geldiğini, önemlerinin ne olduğunu ve nesnelerle komonadlarla nasıl ilişkili olduğunu açıklayabilir mi?


21
Jeremy Gibbons'ın mükemmel kitap desenlerini FP'de tavsiye edebilir miyim: patternsinfp.wordpress.com ve onun oldukça anlaşılır makalesi "Fonksiyonel Programları Hesaplamak"? Her ikisi de kömür direğini oldukça sıkı bir şekilde (ör. Bir blog yazısı ile karşılaştırıldığında) kapsar, ancak biraz Haskell'i bilen biri için oldukça bağımsızdır.
Kristopher Micinski

2
@ KristopherMicinski, çok ilginç. Teşekkürler!
missingfaktor

Yanıtlar:


474

Cebir

Bence başlangıç ​​yeri bir cebir fikrini anlamak olacak . Bu sadece gruplar, halkalar, monoidler ve benzeri cebirsel yapıların genelleştirilmesidir. Çoğu zaman, bu şeyler setler olarak tanıtılır, ancak arkadaşlar arasında olduğumuz için bunun yerine Haskell türleri hakkında konuşacağım. (Yine de bazı Yunan harflerini kullanmaya dayanamıyorum - her şeyi daha havalı gösteriyorlar!)

O halde bir cebir sadece τbazı işlevleri ve kimlikleri olan bir türdür . Bu işlevler farklı türde argümanlar alır τve a τ: curcried, hepsi gibi görünür (τ, τ,…, τ) → τ. Ayrıca "kimliklere" τsahip olabilirler - bunların bazı işlevlerle özel davranışları vardır.

Bunun en basit örneği monoid. Monoid, τişlevi mappend ∷ (τ, τ) → τve kimliği olan herhangi bir türdür mzero ∷ τ. Diğer örnekler, gruplar (ekstra bir invert ∷ τ → τfonksiyon haricinde monoidler gibidir ), halkalar, kafesler vb.

Tüm işlevler çalışır, τancak farklı özelliklere sahip olabilir. Biz bu yazabiliriz τⁿ → τnerede, τⁿbir tuplea eşler n τ. Bu şekilde, kimlikleri sadece boş grubun τ⁰ → τolduğu gibi düşünmek mantıklıdır . Yani şimdi bir cebir fikrini basitleştirebiliriz: sadece üzerinde bazı fonksiyonların bulunduğu bir tür.τ⁰()

Bir cebir, tıpkı kodla yaptığımız gibi, matematikte "dışlanmış" yaygın bir kalıptır. İnsanlar bir sürü ilginç şeyin - yukarıda adı geçen monoidler, gruplar, kafesler ve benzeri - hepsinin benzer bir desen izlediğini fark ettiler, böylece soyutladılar. Bunu yapmanın avantajı programlamadakiyle aynıdır: yeniden kullanılabilir kanıtlar oluşturur ve belirli akıl yürütmeyi kolaylaştırır.

F-Cebir

Ancak, faktoring ile pek işimiz bitmedi. Şimdiye kadar bir grup fonksiyonumuz var τⁿ → τ. Aslında hepsini tek bir fonksiyonda birleştirmek için düzgün bir numara yapabiliriz. Özellikle, monoidlere bakalım: mappend ∷ (τ, τ) → τve var mempty ∷ () → τ. Bunları bir toplam türü kullanarak tek bir işleve dönüştürebiliriz Either. Şöyle görünecektir:

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

Biz aslında birleştirmek için tekrar tekrar bu dönüşümü kullanabilirsiniz tümτⁿ → τ için, bir tek içine fonksiyonları herhangi cebir. (Aslında, biz fonksiyonların herhangi bir sayı için yapabilir a → τ, b → τiçin bu kadar ve herhangi a, b,… .)

Bu bize bir tür olarak cebirlerin bahsedelim τbir ile tek bazı karmaşa fonksiyonu Eithers tek için τ. Monoidler için bu karışıklık Either (τ, τ) ():; (ekstra bir τ → τişlemi olan) gruplar için :Either (Either (τ, τ) τ) () . Her farklı yapı için farklı bir tiptir. Peki tüm bu türlerin ortak noktası nedir? En bariz şey, hepsi sadece ürünlerin toplamıdır - cebirsel veri türleri. Örneğin, monoidler için, herhangi bir monoid τ için çalışan bir monoid argüman türü oluşturabiliriz :

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

Aynı şeyi gruplar, halkalar ve kafesler ve diğer tüm olası yapılar için de yapabiliriz.

Tüm bu türler hakkında başka özel olan nedir? HepsiFunctors ! Örneğin:

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

Böylece bir cebir fikrimizi daha da genelleştirebiliriz. Sadece bazı τişlevler f τ → τiçin bir fonksiyona sahip bir türf . Aslında, bunu bir tip sınıf olarak yazabiliriz:

class Functor f  Algebra f τ where
  op  f τ  τ

Buna genellikle "F-cebiri" denir, çünkü fonksiyon tarafından belirlenir F . Tipik olarak kısmen uygulayabilirsek, benzer bir şey tanımlayabiliriz class Monoid = Algebra MonoidArgument.

Coalgebras

Şimdi, umarım bir cebirin ne olduğu ve bunun normal cebirsel yapıların nasıl genelleştirildiği hakkında iyi bir kavrayışa sahip olursunuz. Öyleyse bir F-kömürgebra nedir? İşbirliği bunun bir cebirin "ikili" olduğunu ima ediyor - yani bir cebiri alıp bazı okları çeviriyoruz. Yukarıdaki tanımda sadece bir ok görüyorum, bu yüzden şunu çevireceğim:

class Functor f  CoAlgebra f τ where
  coop  τ  f τ

Ve hepsi bu! Şimdi, bu sonuç biraz huysuz görünebilir (heh). Sana söylerBir kömür kömürünün ne olduğunu , ancak bunun nasıl yararlı olduğu veya neden önemsediğimiz hakkında herhangi bir fikir vermez. Bunu iyi bir örnek ya da iki örnek bulduğumda ya da bulduğumda biraz anlayacağım: P.

Sınıflar ve Nesneler

Biraz okuduktan sonra, sınıfları ve nesneleri temsil etmek için kömürgebeleri nasıl kullanacağım konusunda iyi bir fikrim olduğunu düşünüyorum. Biz bir türü Ctüm sınıfta nesnelerin olası iç durumlarını içerir; sınıfın kendisi üzerinde bir kömürC nesnelerin yöntemlerini ve özelliklerini belirten .

Cebir örneğinde gösterildiği gibi, eğer herhangi biri için a → τve bir sürü fonksiyonumuz varsa , hepsini bir toplam türü kullanarak tek bir fonksiyonda birleştirebiliriz . İkili "kavramı" tipindeki fonksiyonları bir demet birleştirerek olacağını , vb. Bunu, toplam türünün ikisini (ürün türü) kullanarak yapabiliriz. Bu nedenle, yukarıdaki iki işlev ( ve denir ) göz önüne alındığında , şöyle tek bir işlev oluşturabiliriz:b → τa, b,…Eitherτ → aτ → bfg

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

Tip (a, a), basit bir şekilde bir functor, bu yüzden kesinlikle bir F-kömürgebra anlayışımıza uyuyor. Bu özel hile, bir grup farklı işlevi (veya OOP için yöntemleri) tek bir tür işlevde paketlememizi sağlar.τ → f τ .

Türümüzün öğeleri , nesnenin dahili durumunu Ctemsil eder . Nesnenin okunabilir bazı özellikleri varsa, duruma bağlı olmaları gerekir. Bunu yapmanın en belirgin yolu onları bir işlev haline getirmektir . Öyleyse length özelliği (ör. ) İstiyorsak ,Cobject.lengthC → Int .

Argüman alabilecek ve durumu değiştirebilecek yöntemler istiyoruz. Bunu yapmak için tüm argümanları almalı ve yeni bir tane üretmeliyiz C. En bir düşünelim setPositionbir sürer yöntemi xve ykoordinat: object.setPosition(1, 2). Şöyle görünecektir:C → ((Int, Int) → C) .

Buradaki önemli model, nesnenin "yöntemler" ve "özelliklerinin" nesnenin kendisini ilk argümanları olarak almasıdır. Bu tıpkı selfPython'daki parametre gibidir ve thisdiğer birçok dilin örtük hali gibi . Bir kömürgebra aslında bir selfparametre alma davranışını enkapsüle eder : ilk Cgiren C → F Cbudur.

Şimdi hepsini bir araya getirelim. Bir positionözelliği, bir nameözelliği ve setPositionişlevi olan bir sınıf düşünelim :

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

Bu sınıfı temsil etmek için iki bölüme ihtiyacımız var. İlk olarak, nesnenin iç durumunu temsil etmemiz gerekir; bu durumda sadece iki Ints ve a tutar String. (Bu bizim Ctipimiz.) O zaman sınıfı temsil eden kömürgebra ile gelmeliyiz.

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

Yazmak için iki özelliğimiz var. Oldukça önemsizler:

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

name  C  String
name self = _name self

Şimdi sadece konumu güncelleyebilmemiz gerekiyor:

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

Bu, açık selfdeğişkenleri olan bir Python sınıfı gibidir . Artık bir grup self →fonksiyonumuz olduğuna göre, bunları kömürgebra için tek bir fonksiyonda birleştirmemiz gerekiyor. Bunu basit bir grupla yapabiliriz:

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

Tipi ((Int, Int), String, (Int, Int) → c)-için herhangi c yüzden, bir functor bu mu coopistediğimiz formu var: Functor f ⇒ C → f C.

Bu göz önüne alındığında , yukarıda verdiğim sınıfı belirten bir kömürgebra Cile birlikte coop. Nesnelerimizin sahip olması gereken herhangi bir sayıda yöntemi ve özelliği belirtmek için aynı tekniği nasıl kullanabileceğimizi görebilirsiniz.

Bu, sınıflarla başa çıkmak için kömürle ilgili akıl yürütmeyi kullanmamızı sağlar. Örneğin, sınıflar arasındaki dönüşümleri temsil etmek için bir "F-kömürgebra homomorfizması" kavramını getirebiliriz. Bu, korkutucu sondaj terimidir, sadece yapıyı koruyan kömürler arasında bir dönüşüm anlamına gelir. Bu, sınıfları diğer sınıflarla eşlemeyi düşünmeyi çok daha kolay hale getirir.

Kısacası, bir F-kömürübra self, her bir nesnenin iç durumunu içeren bir parametreye bağlı olan bir grup özellik ve yönteme sahip bir sınıfı temsil eder .

Diğer Kategoriler

Şimdiye kadar Haskell türleri olarak cebir ve kömürden bahsettik. Bir cebir sadece türüdür τbir işlevle f τ → τve coalgebra sadece türüdür τbir işlevle τ → f τ.

Bununla birlikte, hiçbir şey bu fikirleri Haskell ile gerçekten bağlamaz . Aslında, genellikle türler ve Haskell işlevleri yerine kümeler ve matematiksel işlevler olarak tanıtılırlar. Gerçekten de, bu kavramları herhangi bir kategoride !

Bazı kategoriler için bir F-cebiri tanımlayabiliriz C. İlk olarak, bir functor'a ihtiyacımız var F : C → C- yani bir endofunctor . (Tüm Haskell Functorler aslında dan endofunctors vardır Hask → Hask.) Ardından, bir cebir sadece bir nesnedir Agelen Cbir morfizma ile F A → A. Bir kömürgebra,A → F A .

Diğer kategorileri dikkate alarak ne kazanırız? Aynı fikirleri farklı bağlamlarda kullanabiliriz. Monadlar gibi. Haskell'de bir monad, M ∷ ★ → ★üç operasyonu olan bir çeşittir :

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

mapFonksiyon aslında sadece bir kanıtıdır Mbir olduğunu Functor. Yani bir monadın sadece iki operasyonu olan bir fonktör olduğunu söyleyebiliriz : returnvejoin .

Functor'lar kendi aralarında bir kategori oluştururlar ve aralarındaki morfizmler "doğal dönüşümler" olarak adlandırılır. Doğal dönüşüm, yapısını korurken bir işlevi diğerine dönüştürmenin bir yoludur. İşte fikri açıklamaya yardımcı olan güzel bir makale. Hakkında konuşuyor concat, ki bu sadecejoin listeler için.

Haskell functorları ile, iki functorun bileşimi bir functor'un kendisidir. Sözde kodda şunu yazabiliriz:

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

Bu join, bir haritalama olarak düşünmemize yardımcı olur f ∘ f → f. Tipi joinDİR ∀α. f (f α) → f α. Sezgisel olarak, tüm tipler için geçerli olan bir fonksiyonun α,f .

returnbenzer bir dönüşümdür. Türü ∀α. α → f α. Bu farklı görünüyor - birincisi αbir işlevcinin içinde değil Ne mutlu ki, orada bir kimlik functor ekleyerek bunu düzeltebilirsiniz: ∀α. Identity α → f α. Yani returnbir dönüşümdürIdentity → f .

Şimdi bazı functor etrafında dayalı adil bir cebir gibi bir monad düşünebiliriz foperasyonları ile f ∘ f → fve Identity → f. Bu tanıdık gelmiyor mu? Sadece bir tür τoperasyon olan τ × τ → τve() → τ .

Yani bir monad tıpkı bir monoid gibidir, bir tipe sahip olmak yerine bir fonksiyoncumuz vardır. Sadece farklı bir kategoride aynı tür cebir. (Burası "Bir monad, endofunktörler kategorisinde sadece bir monoiddir" ifadesinin bildiğim kadarıyla geldiği yerdir.)

Şimdi bu iki operasyonumuz var: f ∘ f → fve Identity → f. İlgili kömürgebra'yı elde etmek için sadece okları çeviriyoruz. Bu bize iki yeni işlem verir: f → f ∘ fve f → Identity. Biz bize veren yukarıdaki gibi tip değişkenler ekleyerek Haskell tipleri çevirebilirsiniz ∀α. f α → f (f α)ve ∀α. f α → α. Bu bir comonad'ın tanımına benziyor:

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

Yani bir comonad sonra ise coalgebra endofunctors bir kategoride.


45
Bu inanılmaz derecede değerli. Tüm bu F-cebir işi hakkındaki bazı sezgileri okuma ve örneklerden (örneğin, katamoprhisms ile kullanımlarını görmekten) belirsizleştirmeyi başardım, ama bu benim için bile çok açık. Teşekkürler!
Luis Casillas

28
Bu harika bir açıklama.
Edward KMETT

5
@EdwardKmett: Teşekkürler. Sınıflar ve nesneler hakkında eklediğim şeyler iyi mi? Sadece bugün okudum, ama mantıklı geliyor.
Tikhon Jelvis

7
Değeri için: Burada "endofunctors kategorisi" daha kesin olarak nesneleri bir kategoride endofunctor olan ve okları doğal dönüşümler olan bir kategoridir. Bu monofonik bir kategoridir, functor kompozisyonuna karşılık gelir (,)ve kimlik functor'a karşılık gelir (). Monoidal bir kategorideki bir monoid nesne, Monoid cebirinize karşılık gelen oklara sahip bir nesnedir ve Hask'ta monoidal yapı olarak ürün tiplerine sahip bir monoid nesnesini tanımlar. C'deki endofunktör kategorisindeki monoid bir nesne, C'deki bir monaddır, bu nedenle evet, anlayışınız doğrudur. :]
CA McCann

8
Bu destansı bir son!
jdinunzio

85

F-cebirleri ve F-kömürler, endüktif tipler (veya özyinelemeli tipler ) hakkında akıl yürütmede etkili olan matematiksel yapılardır .

F-cebiri

İlk önce F-cebirleriyle başlayacağız. Mümkün olduğunca basit olmaya çalışacağım.

Sanırım yinelemeli tipin ne olduğunu biliyorsun. Örneğin, bu tamsayıların listesi için bir türdür:

data IntList = Nil | Cons (Int, IntList)

Özyinelemeli olduğu açıktır - gerçekten de tanımı kendini ifade eder. Tanımı, aşağıdaki türlere sahip iki veri yapıcısından oluşur:

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

Ben sadece Nilas () -> IntListdeğil yazdım IntList. Bunlar aslında teorik açıdan eşdeğer türlerdir, çünkü ()türün sadece bir sakini vardır.

Bu işlevlerin imzalarını daha kuramsal bir şekilde yazarsak,

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

burada 1bir birim grubu (bir elemanı ile ayarlanır) ve A × Bişlem iki set enine bir üründür Ave B(olduğundan, çift grubu tüm elemanları geçer ve tüm elemanları geçer(a, b)aAbB ).

İki kümenin ayrık birlik Ave Bkümesidir A | Bsetleri birleşmedir {(a, 1) : a in A}ve {(b, 2) : b in B}. Esasen her iki tüm unsurların bütünüdür Ave Bmensup ya olarak değil, 'işaretlenmiş' bu unsurların her biri ile Aya da B, bu yüzden biz herhangi bir unsur almaya gelirken A | Bbiz hemen bu eleman gelip gelmediğini bilecek Aveya gelenB .

'Birleştirebiliriz' Nilve Consfonksiyonlar, böylece bir set üzerinde çalışan tek bir fonksiyon oluşturacaklar 1 | (Int × IntList):

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

Gerçekten de, Nil|Consfonksiyon ()değere uygulanırsa (ki bu açık bir şekilde 1 | (Int × IntList)kümeye aittir ), sanki sanki öyle davranır Nil; Nil|Consherhangi bir tür değere uygulanırsa (Int, IntList)(bu değerler kümede de yer alıyorsa) olarak 1 | (Int × IntList)davranır Cons.

Şimdi başka bir veri türü düşünün:

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

Aşağıdaki kuruculara sahiptir:

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

ayrıca tek bir fonksiyonda birleştirilebilir:

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

Bu joinedfonksiyonların her ikisinin de benzer tipte olduğu görülebilir : her ikisi de

f :: F T -> T

burada Feden türü alır ve aşağıdakilerden oluşmaktadır daha karmaşık tip verir dönüşümün bir tür xve |işlemleri, kullanımları Tve muhtemelen diğer türleri. Örneğin, IntListve IntTree Faşağıdaki gibi görünür:

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

Herhangi bir cebirsel türün bu şekilde yazılabileceğini hemen fark edebiliriz. Aslında, bu yüzden 'cebir' olarak adlandırılırlar: diğer türlerin bir dizi 'toplamı' (birliği) ve 'ürünleri' (çapraz ürünler) içerirler.

Şimdi F-cebirini tanımlayabiliriz. F-cebiri sadece bir çifttir (T, f), burada Tbir türdür ve tipin fbir fonksiyonudur f :: F T -> T. Örneklerimizde F-cebirleri (IntList, Nil|Cons)ve (IntTree, Leaf|Branch). Bununla birlikte, bu tür ffonksiyonlara rağmen her bir F için aynı olduğunu Tve fkendilerinin keyfi olabileceğini unutmayın. Örneğin (String, g :: 1 | (Int x String) -> String)veya (Double, h :: Int | (Double, Double) -> Double)bazıları için gve hkarşılık gelen F için F-cebiridir.

Daha sonra F-cebir homomorfizmlerini ve daha sonra çok faydalı özelliklere sahip olan ilk F-cebirlerini tanıtabiliriz . Aslında, (IntList, Nil|Cons)bir ilk F1 cebiridir ve (IntTree, Leaf|Branch)bir ilk F2 cebiridir. Gerektiğinden daha karmaşık ve soyut oldukları için bu terimlerin ve özelliklerin tam tanımlarını sunmayacağım.

Bununla birlikte, örneğin, (IntList, Nil|Cons)F-cebiri olduğu gerçeği fold, bu tipte-benzeri bir fonksiyon tanımlamamıza izin verir . Bildiğiniz gibi, katlama, bazı özyinelemeli veri tipini sonlu bir değere dönüştüren bir işlemdir. Örneğin, bir tamsayı listesini listedeki tüm öğelerin toplamı olan tek bir değere katlayabiliriz:

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

Bu işlemi herhangi bir özyinelemeli veri türü üzerinde genelleştirmek mümkündür.

Aşağıdakiler foldrişlevin bir imzasıdır :

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

İlk iki argümanı sonuncusundan ayırmak için kaşlı ayraç kullandığımı unutmayın. Bu gerçek bir foldrişlev değildir , ancak onun için izomorfiktir (yani, kolayca diğerinden alabilirsiniz ve tersi). Kısmen başvuruda foldrşu imza bulunur:

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

Bunun tamsayıların listesini alıp tek bir tamsayı döndüren bir işlev olduğunu görebiliriz. Bu işlevi türümüze göre tanımlayalım IntList.

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

Bu fonksiyonun iki kısımdan oluştuğunu görüyoruz: ilk kısım bu fonksiyonun bir Nilkısmındaki davranışını, IntListikinci kısım fonksiyonun bir Conskısmındaki davranışını tanımlar .

Şimdi, Haskell'de değil, cebirsel tiplerin doğrudan tip imzalarında kullanılmasına izin veren bir dilde programladığımızı varsayalım (iyi, teknik olarak Haskell, cebirsel tiplerin tuples ve Either a bveri tipi aracılığıyla kullanılmasına izin veriyor , ancak bu gereksiz ayrıntılara yol açacak). Bir işlev düşünün:

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

F-cebirinin tanımında olduğu gibi reductortipin bir fonksiyonu olduğu görülebilir F1 Int -> Int! Aslında, çift (Int, reductor)bir F1 cebiridir.

Çünkü IntListbir başlangıç F1 cebir her tür için, olduğu Tve her bir işlev için r :: F1 T -> Tadında bir işlevi vardır vardır, catamorphism için r, dönüştürür IntListiçin T, ve bu fonksiyon benzersizdir. Gerçekten de, bizim örneğimizde bir catamorphism reductorolduğunu sumFold. Nasıl reductorve sumFoldbenzer olduklarına dikkat edin: neredeyse aynı yapıya sahiptirler! Olarak reductortanımlı sparametre kullanım (tip olan tekabül Thesaplama sonucunun kullanımına tekabül) sumFold xsiçinde sumFoldtanımı.

Sadece daha açık hale getirmek ve deseni görmenize yardımcı olmak için, başka bir örnek ve yine ortaya çıkan katlama işlevinden başlıyoruz. appendİlk argümanını ikincisine ekleyen işlevi düşünün :

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

Şu şekilde görünüyor IntList:

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

Yine, redüktörü yazmaya çalışalım:

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

appendFoldbir catamorphism olan appendReductorolan dönüşümleri IntListiçine IntList.

Yani, esasen, F-cebirleri, yinelemeli veri yapıları, yani yapılarımızı bir değere indiren operasyonlar üzerinde 'kıvrımlar' tanımlamamıza izin verir.

F-coalgebras

F-kömürgebras, F-cebirleri için 'ikili' terimdir. unfoldsYinelemeli veri türleri, yani bir değerden yinelemeli yapılar inşa etmenin bir yolunu tanımlamamıza izin veriyorlar .

Aşağıdaki türe sahip olduğunuzu varsayalım:

data IntStream = Cons (Int, IntStream)

Bu sonsuz bir tamsayı akışıdır. Tek kurucusu aşağıdaki tiptedir:

Cons :: (Int, IntStream) -> IntStream

Veya setler açısından

Cons :: Int × IntStream -> IntStream

Haskell veri yapıcıları üzerinde desen eşleşmesine izin verir, böylece IntStreams üzerinde çalışan aşağıdaki işlevleri tanımlayabilirsiniz :

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

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

Doğal olarak bu işlevleri tek tip işlevde 'birleştirebilirsiniz' IntStream -> Int × IntStream:

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

Fonksiyonun sonucunun tipimizin cebirsel temsiliyle nasıl çakıştığına dikkat edin IntStream. Benzer şey diğer özyinelemeli veri türleri için de yapılabilir. Belki de deseni zaten fark ettiniz. Tür fonksiyonlardan oluşan bir aileden söz ediyorum

g :: T -> F T

bir tür nerede T. Şu andan itibaren tanımlayacağız

F1 T = Int × T

Şimdi, F-coalgebra bir çift (T, g), Tbir tür ve gtip bir fonksiyonudur g :: T -> F T. Örneğin (IntStream, head&tail), bir F1 kömürüdür. Yine, tıpkı F-cebirlerinde olduğu gibi, gveT keyfi olabilir, örneğin, (String, h :: String -> Int x String)bazı h için de F1-kömürgebradır.

Tüm F-kömürler arasında ilk F-cebirlerine çift olan terminal F-kömürler de denir . Örneğin IntStream, bir terminal F kömürüdür. Bu, her tür Tve her işlev için p :: T -> F1 T, anamorfizm adı verilen ve dönüştüren bir fonksiyonun var olduğu anlamına gelir.T için IntStreamve böyle işlevini benzersizdir.

Verilen işlevden başlayarak ardışık bir tamsayı akışı oluşturan aşağıdaki işlevi göz önünde bulundurun:

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

Şimdi bir işlevi inceleyelim natsBuilder :: Int -> F1 Int, yani natsBuilder :: Int -> Int × Int:

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

Yine, natsve arasında bazı benzerlikler görebiliriz natsBuilder. Daha önce redüktörler ve katlar ile gözlemlediğimiz bağlantıya çok benzer.natsiçin bir anamorfizmdir natsBuilder.

Başka bir örnek, bir değeri ve işlevi alan ve işlevin birbirini izleyen uygulamalarının akışını değere döndüren bir işlevdir:

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

Oluşturucu işlevi şu şekildedir:

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

O zaman iterateiçin bir anamorfizm iterateBuilder.

Sonuç

Kısacası, F-cebirleri kıvrımların tanımlanmasına, yani özyinelemeli yapıyı tek bir değere indirgeyen operasyonlara izin verir ve F-kömürgebras bunun tersini yapmasına izin verir: tek bir değerden [potansiyel olarak] sonsuz bir yapı inşa eder.

Aslında Haskell'de F-cebirleri ve F-kömürgebrasları çakışır. Bu, her tipte 'alt' değerin varlığının bir sonucu olan çok güzel bir özelliktir. Yani Haskell'de her kıvrımlı tip için hem kıvrımlar hem de katlar oluşturulabilir. Bununla birlikte, bunun arkasındaki teorik model, yukarıda sunduğumdan daha karmaşıktır, bu yüzden kasten kaçındım.

Bu yardımcı olur umarım.


Tip ve tanımı appendReductorbiraz garip görünüyor ve oradaki deseni görmeme gerçekten yardımcı olmadı ... :) Doğru olup olmadığını iki kez kontrol edebilir misiniz? .. Redüktör tipleri genel olarak nasıl görünmelidir? Tanımında r, orada olduğu F1IntList ile, ya da isteğe bağlı bir F?
Max Galkin

37

Öğretici makaleyi gözden geçirme (co) cebirleri ve (co) indüksiyonu üzerine bir eğitim bilgisayar bilimlerinde eş cebir hakkında bir fikir verecektir.

Aşağıda sizi ikna etmek için bir alıntı,

Genel olarak, bazı programlama dillerindeki bir program verileri manipüle eder. Son birkaç on yıl içinde bilgisayar biliminin gelişimi sırasında, örneğin bir kişinin programının üzerinde çalıştığı verilerin belirli bir temsiline bağlı olmadığından emin olmak için bu verilerin soyut bir tanımının arzu edildiği açıklığa kavuşmuştur. Ayrıca, bu tür soyutluk doğruluk kanıtlarını kolaylaştırır.
Bu arzu, cebirsel yöntemlerin bilgisayar biliminde, cebirsel şartname veya soyut veri türü teorisi adı verilen bir dalda kullanılmasına yol açmıştır. Çalışmanın amacı, cebirden tanıdık teknik kavramlarını kullanan kendi başlarındaki veri türleridir. Bilgisayar bilimcileri tarafından kullanılan veri türleri genellikle belirli bir (yapıcı) operasyonlar koleksiyonundan üretilir ve bu nedenle cebirlerin “başlangıcı” bu kadar önemli bir rol oynar.
Standart cebirsel tekniklerin, bilgisayar bilimlerinde kullanılan veri yapılarının çeşitli temel yönlerinin yakalanmasında yararlı olduğu kanıtlanmıştır. Ancak, hesaplamada ortaya çıkan doğal olarak dinamik yapıların bazılarının cebirsel olarak tanımlanması zor olduğu ortaya çıktı. Bu tür yapılar genellikle çeşitli şekillerde dönüştürülebilen bir devlet kavramını içerir. Bu tür devlet tabanlı dinamik sistemlere biçimsel yaklaşımlar genellikle klasik erken referanslar olarak otomata veya geçiş sistemlerini kullanır.
Son on yıl boyunca içgörü, bu tür devlet tabanlı sistemlerin cebir olarak değil, aynı zamanda eş-cebir olarak tanımlanması gerektiği konusunda yavaş yavaş büyüdü. Bunlar, bu derste kesin hale getirilecek şekilde cebirlerin biçimsel ikilisidir. Cebir için "başlangıç" ın ikili özelliği, yani sonluk, bu tür ortak cebirler için çok önemli olmuştur. Ve bu tür nihai ortak cebirler için gerekli olan mantıksal akıl yürütme ilkesi tümevarım değil, tümevarımdır.


Başlangıç ​​teorisi, Kategori teorisi hakkında. Kategori teorisi, functor teorisini yeniden adlandırmalıdır. Kategori olarak, işlevleri tanımlamak için tanımlanması gereken şeylerdir. (Dahası, doğal dönüşümleri tanımlamak için functors tanımlanması gereken şeydir.)

Functor nedir? Bir kümeden diğerine yapılarını koruyan bir dönüşüm. (Daha fazla detay için internette çok iyi bir açıklama var).

F-cebiri nedir? Functor cebiri. Sadece functor'un evrensel uygunluğunun incelenmesi.

Bilgisayar bilimine nasıl bağlanabilir? Program yapılandırılmış bir bilgi kümesi olarak görülebilir. Programın yürütülmesi, bu yapılandırılmış bilgi kümesinin değiştirilmesine karşılık gelir. Yürütmenin program yapısını koruması gerektiği kulağa hoş geliyor. Daha sonra yürütme, bu bilgi kümesi üzerinde bir functor uygulaması olarak görülebilir. (Programı tanımlayan).

Neden F-eş cebiri? Program özü gereği ikili olup, bilgi ile tanımlandığı ve program üzerinde hareket ettikleri için. Daha sonra esas olarak programı oluşturan ve değiştiren bilgiler iki şekilde görüntülenebilir.

  • Program tarafından işlenen bilgiler olarak tanımlanabilecek veriler.
  • Program tarafından paylaşılan bilgi olarak tanımlanabilecek durum.

Sonra bu aşamada şunu söylemek isterim,

  • F-cebiri, Data's Universe (burada tanımlandığı gibi) üzerine etki eden fonksiyonel dönüşümdür.
  • F-ko-cebirleri, Devletin Evrenine etki eden fonksiyonel dönüşümün (burada tanımlandığı gibi) incelenmesidir.

Bir programın ömrü boyunca, veriler ve devlet bir arada var olurlar ve birbirlerini tamamlarlar. Onlar ikili.


5

Açıkçası programlama ile ilgili olan şeylerle başlayacağım ve sonra olabildiğince somut ve yeryüzünde tutmak için bazı matematik şeyler ekleyeceğim.


Bazı bilgisayar bilimcilerini koindüksiyon konusunda alıntı yapalım…

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

Tümevarım sonlu veriler, ortak tümevarım sonsuz verilerdir.

Sonsuz verilerin tipik örneği, tembel listenin (akış) tipidir. Örneğin, bellekte aşağıdaki nesneye sahip olduğumuzu varsayalım:

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

Bilgisayar tüm hold'leri tutamaz, çünkü yalnızca sınırlı miktarda belleğe sahiptir! Ancak yapabileceği şey, arzu ettiğiniz π keyfi olarak uzun bir genişlemesini sağlayacak sonlu bir programdır. Listenin yalnızca sonlu parçalarını kullandığınız sürece, o sonsuz listeyi istediğiniz kadar hesaplayabilirsiniz.

Ancak, aşağıdaki programı göz önünde bulundurun:

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


 print_third_element pi

Bu program pi'nin üçüncü basamağını yazdırmalıdır. Ancak bazı dillerde, bir işleve ilişkin herhangi bir argüman bir işleve geçirilmeden önce değerlendirilir (katı, tembel değil, değerlendirme). Bu azaltma sırasını kullanırsak, yukarıdaki programımız yazıcı fonksiyonumuza geçmeden önce pi rakamlarını sonsuza kadar hesaplar (asla olmaz). Makinede sonsuz bellek olmadığından program sonunda bellek ve kilitlenme bitecektir. Bu en iyi değerlendirme sırası olmayabilir.

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

Haskell gibi tembel fonksiyonel programlama dillerinde her yerde sonsuz veri yapıları vardır. Sonsuz listeler ve daha egzotik veri türleri, bir programın bölümleri arasındaki iletişim için uygun soyutlamalar sağlar. Sonsuz tembel yapılar olmadan benzer bir rahatlığa ulaşmak, çoğu durumda, kontrol akışının akrobatik ters çevrilmesini gerektirecektir.

http://www.alexandrasilva.org/#/talks.html Alexandra Silva'dan kömür örnekleri


Ortam matematiksel bağlamını normal programlama görevleriyle ilişkilendirme

"Cebir" nedir?

Cebirsel yapılar genellikle şöyle görünür:

  1. Şey
  2. İşler neler yapabilir

Bu özellik ve özellikleri 1. yöntemleri ile nesneler gibi görünmelidir. Daha da iyisi, tür imzalar gibi görünmelidir.

Standart matematiksel örnekler arasında monoid ⊃ grup ⊃ vektör-uzay ⊃ "bir cebir" bulunur. Monoidler otomata gibidir: fiil dizileri (örn f.g.h.h.nothing.f.g.f.). Her gitzaman tarih ekleyen ve onu asla silen bir günlük, bir monoid olur, ancak bir grup değildir. Tersler eklerseniz (örn. Negatif sayılar, kesirler, kökler, birikmiş geçmişi silme, kırık bir aynayı parçalama) bir grup alırsınız.

Gruplar birlikte eklenebilecek veya çıkarılabilecek şeyler içerir. Örneğin Durations birlikte eklenebilir. (Ama Dateyapamazlar.) Süreler bir vektör uzayında (sadece bir grupta değil) yaşar, çünkü dış sayılarla da ölçeklenebilirler. (Bir tür imza scaling :: (Number,Duration) → Duration.)

Cebir ⊂ vektör uzayları başka bir şey yapabilir: bazıları var m :: (T,T) → T. Buna "çarpma" deyin ya da etmeyin, çünkü ayrıldıktan Integerssonra "çarpma" (ya da "üs" ) olması daha az açıktır .

(Bu yüzden insanlar (kategori-teorik) evrensel özelliklere bakarlar: onlara çarpmanın ne yapması veya nasıl olması gerektiğini söylemek için :

ürünün evrensel özelliği )


Cebir → Kömür

Comultiplication, rasgele olmayan bir şekilde tanımlamak için çarpma işleminden daha kolaydır, çünkü T → (T,T)sizden gitmek aynı öğeyi tekrarlayabilir. ("diyagonal harita" - spektral teoride diyagonal matrisler / operatörler gibi)

Yine önemli olan senin counit nedir rağmen Counit, genellikle iz (diyagonal girdilerinin toplamı) 'dir yapar ; tracematrisler için iyi bir cevaptır.

Genel olarak ikili bir alana bakmanın nedeni , o alanda düşünmenin daha kolay olmasıdır. Örneğin, normal bir vektör hakkında düşünmek normal olduğu düzlemden daha kolaydır, ancak düzlemleri (hiper uçaklar dahil) vektörlerle kontrol edebilirsiniz (ve şimdi bir ışın izleyicide olduğu gibi tanıdık geometrik vektörden bahsediyorum) .


Yapısal verilerin evcilleştirilmesi (un)

Matematikçiler TQFT'ler gibi eğlenceli bir şey modelliyor olabilirken , programcılar güreşmek zorunda

  • tarih / saat ( + :: (Date,Duration) → Date),
  • yerler ( Paris(+48.8567,+2.3508)! Bu bir şekil, bir nokta değil.),
  • bir anlamda tutarlı olması gereken yapılandırılmamış JSON,
  • yanlış ama yakın XML,
  • bir sürü mantıklı ilişkiyi tatmin etmesi gereken inanılmaz derecede karmaşık CBS verileri,
  • sizin için bir şey ifade eden , ancak perl için oldukça az anlamına gelen düzenli ifadeler .
  • Yöneticinin tüm telefon numaralarını ve villa konumlarını, (şimdi eski) karısı ve çocuklarının isimlerini, doğum günlerini ve her biri inanılmaz derecede iyi olan "açık" ilişkileri (müşteri için açık) tatmin etmesi gereken tüm hediyeleri tutması gereken CRM kodlaması zor,
  • .....

Bilgisayar bilimcileri, kömürgeralardan bahsederken, genellikle Kartezyen ürün gibi set-ish operasyonlarını göz önünde bulundururlar. İnsanların "Cebirler Haskell'deki kömürler" dedikleri zaman bunun demek istediğine inanıyorum. Ama programcılar gibi veri-türleri karmaşık model olması ölçüsünde Place, Date/Timeve Customer-ve bu modeller olarak gerçek dünya gibi çok (ya da gerçek dünyada en az son kullanıcının bakış) olarak görünmesi mümkün-I duallerini inanıyorum sadece set dünyasının ötesinde faydalı olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.