İndeksli monad nedir?


98

İndekslenmiş monad nedir ve bu monad için motivasyon nedir?

Yan etkileri takip etmenin yardımcı olduğunu okudum. Ancak tip imzası ve belgeler beni hiçbir yere götürmüyor.

Yan etkilerin (veya başka herhangi bir geçerli örneğin) takip edilmesine nasıl yardımcı olabileceğine dair bir örnek ne olabilir?

Yanıtlar:


123

Her zamanki gibi, insanların kullandığı terminoloji tamamen tutarlı değil. Monadlardan ilham alan çeşitli kavramlar var ama kesinlikle konuşulmuyor - pek de değil. "İndeksli monad" terimi, böyle bir kavramı karakterize etmek için kullanılan terimlerin bir sayısıdır ("monadish" ve "parametreli monad" (onlar için Atkey'in adı) dahil). (İlgileniyorsanız bu tür bir başka kavram, dönüşün nötr olarak indekslendiği ve bağın indeksinde biriktiği, bir monoid tarafından indekslenen, Katsumata'nın "parametrik etki monad'ı" dır.)

Öncelikle çeşitleri kontrol edelim.

IxMonad (m :: state -> state -> * -> *)

Yani, bir "hesaplama" (veya "eylem", isterseniz, ancak "hesaplamaya" bağlı kalacağım) gibi görünüyor

m before after value

nerede before, after :: stateve value :: *. Buradaki fikir, öngörülebilir bir durum kavramına sahip harici bir sistemle güvenli bir şekilde etkileşim kurmanın yollarını yakalamaktır . Bir hesaplamanın türü, hangi durumun çalışması gerektiğini before, hangi durumun afterçalışacağını ve (normal monadlarda olduğu gibi *) valuehesaplamanın ne tür ürettiğini söyler .

Alışılagelmiş bitler ve parçalar *-bilimi bir monad gibidir , -bilhassa statedomino oynamak gibidir.

ireturn  ::  a -> m i i a    -- returning a pure value preserves state
ibind    ::  m i j a ->      -- we can go from i to j and get an a, thence
             (a -> m j k b)  -- we can go from j to k and get a b, therefore
             -> m i k b      -- we can indeed go from i to k and get a b

Bu şekilde üretilen "Kleisli ok" kavramı (hesaplama sağlayan işlev)

a -> m i j b   -- values a in, b out; state transition i to j

ve bir kompozisyon elde ederiz

icomp :: IxMonad m => (b -> m j k c) -> (a -> m i j b) -> a -> m i k c
icomp f g = \ a -> ibind (g a) f

ve her zaman olduğu gibi, yasalar bunu tam olarak sağlıyor ireturnve icompbize bir kategori veriyor

      ireturn `icomp` g = g
      f `icomp` ireturn = f
(f `icomp` g) `icomp` h = f `icomp` (g `icomp` h)

veya komedide sahte C / Java / her neyse,

      g(); skip = g()
      skip; f() = f()
{g(); h()}; f() = h(); {g(); f()}

Neden uğraşıyorsun? Etkileşim "kurallarını" modellemek. Örneğin, sürücüde bir dvd yoksa çıkaramazsınız ve içinde zaten varsa sürücüye bir dvd koyamazsınız. Yani

data DVDDrive :: Bool -> Bool -> * -> * where  -- Bool is "drive full?"
  DReturn :: a -> DVDDrive i i a
  DInsert :: DVD ->                   -- you have a DVD
             DVDDrive True k a ->     -- you know how to continue full
             DVDDrive False k a       -- so you can insert from empty
  DEject  :: (DVD ->                  -- once you receive a DVD
              DVDDrive False k a) ->  -- you know how to continue empty
             DVDDrive True k a        -- so you can eject when full

instance IxMonad DVDDrive where  -- put these methods where they need to go
  ireturn = DReturn              -- so this goes somewhere else
  ibind (DReturn a)     k  = k a
  ibind (DInsert dvd j) k  = DInsert dvd (ibind j k)
  ibind (DEject j)      k  = DEject j $ \ dvd -> ibind (j dvd) k

Bununla birlikte, "ilkel" komutları tanımlayabiliriz

dInsert :: DVD -> DVDDrive False True ()
dInsert dvd = DInsert dvd $ DReturn ()

dEject :: DVDrive True False DVD
dEject = DEject $ \ dvd -> DReturn dvd

diğerlerinin bir araya getirildiği ireturnve ibind. Şimdi yazabilirim (ödünç alma- donota)

discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dvd' <- dEject; dInsert dvd ; ireturn dvd'

ama fiziksel olarak imkansız değil

discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dInsert dvd; dEject      -- ouch!

Alternatif olarak, kişinin ilkel komutlarını doğrudan tanımlayabilir

data DVDCommand :: Bool -> Bool -> * -> * where
  InsertC  :: DVD -> DVDCommand False True ()
  EjectC   :: DVDCommand True False DVD

ve sonra genel şablonu somutlaştırın

data CommandIxMonad :: (state -> state -> * -> *) ->
                        state -> state -> * -> * where
  CReturn  :: a -> CommandIxMonad c i i a
  (:?)     :: c i j a -> (a -> CommandIxMonad c j k b) ->
                CommandIxMonad c i k b

instance IxMonad (CommandIxMonad c) where
  ireturn = CReturn
  ibind (CReturn a) k  = k a
  ibind (c :? j)    k  = c :? \ a -> ibind (j a) k

Aslında, ilkel Kleisli oklarının ne olduğunu (bir "domino" nun ne olduğunu) söyledik, sonra bunların üzerine uygun bir "hesaplama dizisi" kavramı inşa ettik.

Her indekslenmiş monad için m, "değişim yok diyagonal" m i ibir monaddır, ancak genel m i jolarak değildir. Dahası, değerler indekslenmez, ancak hesaplamalar indekslenir, bu nedenle indekslenmiş bir monad, başka bir kategori için somutlaştırılmış monad'ın olağan fikri değildir.

Şimdi, Kleisli ok tipine tekrar bakın

a -> m i j b

iBaşlamak için durumda olmamız gerektiğini biliyoruz ve herhangi bir devamın durumdan başlayacağını tahmin ediyoruz j. Bu sistem hakkında çok şey biliyoruz! Bu riskli bir operasyon değil! DVD'yi sürücüye koyduğumuzda içeri girer! DVD sürücüsü, her komuttan sonra durumun ne olduğu konusunda herhangi bir söz sahibi değildir.

Ancak dünya ile etkileşime girerken bu genel olarak doğru değildir. Bazen kontrolü biraz başkasına vermeniz ve dünyanın istediğini yapmasına izin vermeniz gerekebilir. Örneğin, bir sunucuysanız, müşterinize bir seçenek sunabilirsiniz ve oturum durumunuz, onların seçtiği şeye bağlı olacaktır. Sunucunun "teklif seçimi" işlemi, sonuçtaki durumu belirlemez, ancak sunucu yine de devam edebilmelidir. Bu, yukarıdaki anlamda "ilkel bir komut" değildir, bu nedenle indekslenmiş monadlar, öngörülemeyen senaryoyu modellemek için o kadar iyi bir araç değildir .

Daha iyi bir araç nedir?

type f :-> g = forall state. f state -> g state

class MonadIx (m :: (state -> *) -> (state -> *)) where
  returnIx    :: x :-> m x
  flipBindIx  :: (a :-> m b) -> (m a :-> m b)  -- tidier than bindIx

Korkunç bisküviler mi? Pek değil, iki nedenden dolayı. Çünkü Birincisi, bir monad nedir gibi oldukça fazla görünüyor olduğunu üzerinden bir monad, ama (state -> *)yerine *. İki, Kleisli ok türüne bakarsanız,

a :-> m b   =   forall state. a state -> m b state

Good Old Hoare Logic'te olduğu gibi, bir ön koşul a ve son koşullu hesaplama türlerini elde edersiniz b. Program mantığındaki iddiaların Curry-Howard yazışmalarını aşması ve Haskell tipi haline gelmesi yarım yüzyıldan kısa sürdü. Tipi returnIx"atlamak" için Hoare Mantık kuraldır "sadece hiçbir şey yaparak, tutan herhangi sonşartı elde edebilirsiniz" diyor. Karşılık gelen kompozisyon, ";" için Hoare Mantığı kuralıdır.

bindIxTüm niceleyicileri koyarak türüne bakarak bitirelim.

bindIx :: forall i. m a i -> (forall j. a j -> m b j) -> m b i

Bunların forallzıt kutupları var. Başlangıç ​​durumunu ive ison koşulla başlayabilecek bir hesaplamayı seçiyoruz a. Dünya istediği herhangi bir ara durumu seçer j, ancak bize sonradan koşulun bgeçerli olduğuna dair kanıt vermelidir ve bu tür herhangi bir durumdan btutunmaya devam edebiliriz. Böylece, sırayla, bdurumdan koşul elde edebiliriz i. "Sonra" durumları kavramamızı bırakarak, öngörülemeyen hesaplamaları modelleyebiliriz .

Hem IxMonadve MonadIxyararlıdır. Her iki model de interaktif hesaplamaların değişen duruma göre geçerliliği, sırasıyla öngörülebilir ve öngörülemez. Tahmin edilebilirlik, elde edebildiğiniz zaman değerlidir, ancak tahmin edilemezlik bazen hayatın bir gerçeğidir. Öyleyse umarım, bu cevap indekslenmiş monadların ne zaman işe yaramaya başladıklarını ve ne zaman duracaklarını tahmin ederek bazı göstergeler verir.


1
True/ FalseDeğerlerini tür bağımsız değişkenleri olarak nasıl iletebilirsiniz DVDDrive? Bu bir uzantı mı, yoksa boole'lar gerçekten burada mı?
Bergi

8
@Bergi Booleanların tür düzeyinde var olması için "kaldırıldı". Bu, Haskell'de DataKindsuzantıyı kullanarak ve buna bağlı olarak yazılan dillerde mümkündür ... şey, hepsi bu kadar.
J. Abrahamson

Biraz genişletebilir misin MonadIx, belki örneklerle? Teorik açıdan daha mı iyi yoksa pratik uygulama için daha mı iyi?
Christian Conkle

2
@ChristianConkle Bunun çok yardımcı olmadığının farkındayım. Ama gerçekten başka bir soruyu soruyorsunuz. Yerel olarak MonadIx'in "daha iyi" olduğunu söylediğimde, öngörülemeyen bir ortamla etkileşimleri modelleme bağlamında kast ediyorum. Örneğin, dvd sürücünüzün DVD'leri dışarı atmasına izin veriliyorsa, onları takmaya çalıştığınızda hoşuna gitmez. Bazı pratik durumlar bu kadar kötü davranılır. Diğerlerinin daha fazla tahmin edilebilirliği vardır (yani, herhangi bir sürekliliğin hangi durumda başladığını söyleyebilirsiniz, işlemlerin başarısız olmadığını söyleyebilirsiniz), bu durumda IxMonad ile çalışmak daha kolaydır.
pigworker

1
Yanıttaki do gösterimini "ödünç aldığınızda", RebindableSyntaxuzantı ile gerçekten geçerli bir sözdizimi olduğunu söylemek yararlı olabilir . Diğer gerekli uzantılardan bahsetmek, daha önce de belirtildiği gibi iyi olurduDataKinds
gigabayt

46

Bildiğim dizine alınmış bir monad tanımlamanın en az üç yolu vardır.

Ben bu seçeneklerden referans alırız la X à endeksli monads ben onları düşünmek eğilimindedir nasıl olduğu gibi, X'in bilgisayar bilimciler Bob Atkey, Conor McBride ve Dominic Orchard üzerinde durulurken,. Bu yapıların bazı kısımlarının çok daha uzun bir şanlı tarihi ve kategori teorisi yoluyla daha hoş yorumları var, ancak bunları ilk olarak bu isimlerle ilişkilendirildiğini öğrendim ve bu cevabın fazla ezoterik olmasını önlemeye çalışıyorum.

Atkey

Bob Atkey'in indekslenmiş monad tarzı, monad indeksiyle başa çıkmak için 2 ekstra parametre ile çalışmaktır.

Bununla birlikte, diğer cevaplarda insanların savurduğu tanımları elde edersiniz:

class IMonad m where
  ireturn  ::  a -> m i i a
  ibind    ::  m i j a -> (a -> m j k b) -> m i k b

Ayrıca Atkey à la indekslenmiş comonadlar da tanımlayabiliriz. Aslında bu dışarı kilometre bir sürü içinde lenskod temeli .

McBride

Endekslenmiş monadın bir sonraki formu, Conor McBride'ın "Kleisli Arrows of Outrageous Fortune" adlı makalesindeki tanımıdır . Bunun yerine, dizin için tek bir parametre kullanır. Bu, indekslenmiş monad tanımının oldukça akıllı bir şekle sahip olmasını sağlar.

Aşağıdaki gibi parametriklik kullanarak doğal bir dönüşümü tanımlarsak

type a ~> b = forall i. a i -> b i 

o zaman McBride'ın tanımını şöyle yazabiliriz:

class IMonad m where
  ireturn :: a ~> m a
  ibind :: (a ~> m b) -> (m a ~> m b)

Bu, Atkey'den oldukça farklı hissettiriyor, ancak daha çok normal bir Monad gibi geliyor, üzerine bir monad (m :: * -> *)inşa etmek yerine, üzerine inşa ediyoruz (m :: (k -> *) -> (k -> *).

İlginç bir şekilde, McBride'ın taklit edilemez tarzıyla "anahtar" olarak okumanız gerektiğini söylemeyi seçtiği akıllı bir veri türü kullanarak Atkey'in indekslenmiş monad stilini McBride'dan kurtarabilirsiniz.

data (:=) :: a i j where
   V :: a -> (a := i) i

Şimdi bunu çözebilirsin

ireturn :: IMonad m => (a := j) ~> m (a := j)

hangisine genişler

ireturn :: IMonad m => (a := j) i -> m (a := j) i

sadece j = i olduğunda çağrılabilir ve sonra dikkatli bir şekilde okunduğunda ibindAtkey'inki ile aynı şeyi geri getirebilir ibind. Bu (: =) veri yapılarının etrafından dolaşmanız gerekir, ancak bunlar Atkey sunumunun gücünü geri kazanır.

Öte yandan, Atkey sunumu McBride'ın sürümünün tüm kullanımlarını kurtarmak için yeterince güçlü değil. Güç kesinlikle kazanıldı.

Bir başka güzel şey de McBride'ın indekslenmiş monadının açıkça bir monad olması, sadece farklı bir functor kategorisindeki bir monad olmasıdır. Bu gelen functors kategorisine endofunctors üzerinde çalıştığı (k -> *)için (k -> *)oldukça den functors kategorisinde daha *hiç *.

Dizine alınmış komonadlar için McBride'dan Atkey'e dönüşümün nasıl yapılacağını bulmak eğlenceli bir alıştırma . McBride'ın makalesinde "anahtarda" yapısı için kişisel olarak bir veri türü "At" kullanıyorum. Aslında ICFP 2013'te Bob Atkey'e gittim ve onu ters yüz ederek onu bir "Ceket" haline getirdiğimi söyledim. Görünür şekilde rahatsız görünüyordu. Hat kafamda daha iyi oynadı. =)

Meyve bahçesi

Son olarak, "endeksli monad" adına çok daha az sıklıkla başvurulan üçüncü bir davacı, onun yerine endeksleri parçalamak için bir tür düzeyinde monoid kullandığı Dominic Orchard'dan kaynaklanmaktadır. Yapının ayrıntılarına girmek yerine, bu konuşmaya basitçe bağlantı vereceğim:

https://github.com/dorchard/effect-monad/blob/master/docs/ixmonad-fita14.pdf


1
Orchard'ın monadının Atkey'inkine eşdeğer olduğu konusunda haklı mıyım, çünkü endomorfizmi monoid alarak birinciden ikincisine gidebilir ve durum geçişinde monoidal ekleri kodlayan CPS ile geriye doğru gidebilir miyiz?
András Kovács

Bu bana mantıklı geliyor.
Edward KMETT

Bununla birlikte, bana ICFP 2013'te söylediği bir şeye dayanarak, Orchard'ın tip ailelerinin bazı okların bağlanamadığı rastgele bir kategori yerine gerçek bir monoid gibi davranmasını amaçladığına inanıyorum, bu yüzden hikayede daha fazlası olabilir. bundan da öte, Atkey'in yapısı bazı Kleisli eylemlerinin diğerleriyle bağlantı kurmasını kolayca kısıtlamanıza izin veriyor - birçok yönden işin özü ve McBride'ın versiyonu.
Edward KMETT

2
"Dikkatli okuma" konusunu genişletmek için ibind: Tip takma adını tanıtın Atkey m i j a = m (a := j) i. Bunu mAtkey'in tanımında olduğu gibi kullanmak , aradığımız iki imzayı kurtarır: ireturnAtkin :: a -> m (a := i) ive ibindAtkin :: m (a := j) i -> (a -> m (b := k) j) -> m (b := k) i. Birinci bir bileşim ile elde edilir: ireturn . V. İkincisi (1) forall j. (a := j) j -> m (b := k) jörüntü eşleştirme yoluyla bir işlev oluşturarak , sonra kurtarılanı aikinci bağımsız değişkenine geçirerek ibindAtkin.
Dünya

23

Basit bir senaryo olarak, bir durum monadınız olduğunu varsayın. Durum türü karmaşık bir büyüktür, ancak tüm bu durumlar iki kümeye ayrılabilir: kırmızı ve mavi durumlar. Bu monaddaki bazı işlemler yalnızca mevcut durum mavi bir durumsa anlamlıdır. Bunların arasında bazıları durumu mavi ( blueToBlue) tutarken, diğerleri durumu kırmızı ( blueToRed) yapacak . Sıradan bir monadda yazabiliriz

blueToRed  :: State S ()
blueToBlue :: State S ()

foo :: State S ()
foo = do blueToRed
         blueToBlue

ikinci eylem mavi bir durum beklediğinden bir çalışma zamanı hatası tetikleniyor. Bunu statik olarak önlemek istiyoruz. Endekslenmiş monad bu hedefi yerine getirir:

data Red
data Blue

-- assume a new indexed State monad
blueToRed  :: State S Blue Red  ()
blueToBlue :: State S Blue Blue ()

foo :: State S ?? ?? ()
foo = blueToRed `ibind` \_ ->
      blueToBlue          -- type error

İkinci blueToRed( Red) dizini ilk blueToBlue( Blue) dizininden farklı olduğu için bir tür hatası tetiklenir .

Başka bir örnek olarak, indekslenmiş monadlar ile bir durum monadının durumu için türü değiştirmesine izin verebilirsiniz, örneğin

data State old new a = State (old -> (new, a))

Yukarıdakileri, statik olarak yazılan heterojen bir yığın olan bir durum oluşturmak için kullanabilirsiniz. Operasyonların türü olurdu

push :: a -> State old (a,old) ()
pop  :: State (a,new) new a

Başka bir örnek olarak, dosya erişimine izin vermeyen sınırlı bir IO monad istediğinizi varsayalım. Örneğin kullanabilirsiniz

openFile :: IO any FilesAccessed ()
newIORef :: a -> IO any any (IORef a)
-- no operation of type :: IO any NoAccess _

Bu şekilde, türe sahip bir eylemin IO ... NoAccess ()dosya erişimsiz olması statik olarak garanti edilir. Bunun yerine, türden bir eylem IO ... FilesAccessed ()dosyalara erişebilir. İndekslenmiş bir monad'a sahip olmak, kısıtlı GÇ için ayrı bir tür oluşturmanıza gerek olmadığı anlamına gelir; bu, her iki GÇ türünde dosya ile ilgili olmayan her işlevi çoğaltmanızı gerektirir.


18

İndekslenmiş bir monad, örneğin durum monadı gibi belirli bir monad değil, monad kavramının ekstra tip parametrelerle bir tür genelleştirmesidir.

"Standart" monadic değer türüne sahip Oysa Monad m => m adizinlenmiş bir monad bir değer olacağını IndexedMonad m => m i j anerede ive jböylece endeks tipleridir imonadic hesaplama başında ve endeks türüdür jhesaplama sonunda. Bir bakıma, ibir tür girdi türü ve jçıktı türü olarak düşünebilirsiniz .

Kullanarak State, örnek olarak, durum bilgisi olan bir hesaplama State s atip bir ortam sağlamaktadır shesaplama boyunca ve tip bir sonuç verir a. Dizine alınmış bir sürüm, IndexedState i j adurumun hesaplama sırasında farklı bir türe değişebileceği durum bilgisi içeren bir hesaplamadır. Başlangıç ​​durumu, tür ive duruma sahiptir ve hesaplamanın sonu türe sahiptir j.

Normal bir monad üzerinde indekslenmiş bir monad kullanmak nadiren gereklidir, ancak bazı durumlarda daha katı statik garantileri kodlamak için kullanılabilir.


5

Bağımlı türlerde indekslemenin nasıl kullanıldığına bakmak önemli olabilir (örn. Agda'da). Bu, indekslemenin genel olarak nasıl yardımcı olduğunu açıklayabilir, ardından bu deneyimi monadlara çevirebilir.

Dizin oluşturma, belirli tür örnekleri arasında ilişki kurmaya izin verir. Ardından, bu ilişkinin devam edip etmediğini belirlemek için bazı değerler hakkında mantık yürütebilirsiniz.

Örneğin (agda'da) bazı doğal sayıların ilişkili olduğunu belirtebilirsiniz _<_ve tür bunların hangi sayı olduğunu söyler. O zaman bazı işlevlere tanıklık edilmesini isteyebilirsiniz m < n, çünkü ancak o zaman işlev doğru çalışır - ve böyle bir tanıklık sağlamadan program derlenmez.

Başka bir örnek olarak, seçtiğiniz dil için yeterli sebat ve derleyici desteği verildiğinde, işlevin belirli bir listenin sıralandığını varsaydığını kodlayabilirsiniz.

İndekslenmiş monadlar, yan etkileri daha kesin bir şekilde yönetmek için bağımlı tip sistemlerin yaptıklarının bazılarını kodlamaya izin verir.

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.