Serbest monad ve Reaktif Uzantılar arasındaki ilişki nedir?


14

LINQ Rx.NET içine gelişti, ancak her zaman FP biraz ilgi vardı bir C # arka plan geliyor. Monad'lara ve F #'daki bazı yan projelere giriş yaptıktan sonra, bir sonraki seviyeye geçmeye hazırdım.

Şimdi, Scala insanlarından gelen ücretsiz monad ve Haskell veya F #'daki birden fazla yazı üzerine yapılan birkaç görüşmeden sonra, anlamaların IObservablezincirlere oldukça benzer olması için tercümanlarla gramerler buldum .

FRP'de, zincirin içinde kalan yan etkiler ve hatalar da dahil olmak üzere, etki alanına özgü daha küçük parçalardan bir işlem tanımı oluşturursunuz ve uygulamanızı bir dizi işlem ve yan etki olarak modellersiniz. Serbest monad'da, doğru anladıysam, işlemlerinizi functor olarak yaparak ve coyoneda kullanarak kaldırarak aynı şeyi yaparsınız.

İğneyi herhangi bir yaklaşıma doğru eğen ikisi arasındaki farklar nelerdir? Hizmetinizi veya programınızı tanımlarken temel fark nedir?


2
Sorun hakkında düşünmenin ilginç bir yolu ... FRP genellikle bu şekilde formüle edilmemiş olsa bile bir monad olarak görülebilir . Çoğu (hepsi olmasa da) monadlar serbest monad için izomorfiktir . ContÖzgür monad ile ifade edilemediğini önerdiğim tek monad olduğu gibi , muhtemelen FRP'nin olabileceğini varsayabiliriz. As can başka hemen hemen her şey .
Jules

2
LINQ ve Rx.NET'in tasarımcısı Erik Meijer'e IObservablegöre, devam monadının bir örneğidir.
Jörg W Mittag

1
Şu anda ayrıntıları çözmek için zamanım yok, ama tahminim hem RX uzantılarının hem de ücretsiz monad yaklaşımının çok benzer hedeflere ulaşması, ancak biraz farklı yapılara sahip olması. RX Gözlemlenebilirlerin kendilerinin monad olması mümkündür ve daha sonra gözlemlenebilir olanları kullanarak ücretsiz bir monad hesaplamasını eşleştirebilirsiniz - "kabaca" serbest monad "daki" serbest "anlamına gelir. Ya da belki de ilişki o kadar doğrudan değildir ve sadece benzer amaçlar için nasıl kullanıldıklarını öğreniyorsunuzdur.
Tikhon Jelvis

Yanıtlar:


6

monads

Bir monad şunlardan oluşur:

  • Bir endofunctor . Yazılım mühendisliği dünyamızda, bunun tek, sınırsız tip parametresine sahip bir veri tipine karşılık geldiğini söyleyebiliriz. C # 'da, bu formun bir şey olacaktır:

    class M<T> { ... }
    
  • Bu veri türü üzerinde tanımlanan iki işlem:

    • return/ pure"saf" bir değer alır (yani bir Tdeğer) ve bunu monad'a "sarar" (yani, bir M<T>değer üretir ). Yana returnC # ayrılmış bir anahtar kelimedir, ben kullanacağız pureşu andan itibaren bu operasyon başvurmak için. C # 'da, pureimzası olan bir yöntem olurdu:

      M<T> pure(T v);
      
    • bind/ flatmapbir monadik değer ( M<A>) ve bir işlev alır f. fsaf bir değer alır ve monadik bir değer ( M<B>) döndürür . Bunlardan bindyeni bir monadik değer ( M<B>) üretir . bindaşağıdaki C # imzasına sahiptir:

      M<B> bind(M<A> mv, Func<A, M<B>> f);
      

Ayrıca, bir monad olmak pureve bindüç monad yasalarına uymak zorundadır.

Şimdi, C # 'daki monadları modellemenin bir yolu bir arayüz oluşturmak olacaktır:

interface Monad<M> {
  M<T> pure(T v);
  M<B> bind(M<A> mv, Func<A, M<B>> f);
}

(Not: İşleri kısa ve etkileyici tutmak için, bu yanıt boyunca kodla ilgili bazı özgürlükler alacağım.)

Şimdi somut veri türleri için monad uygulayabiliriz Monad<M>. Örneğin, aşağıdaki monad'ı aşağıdakiler için uygulayabiliriz IEnumerable:

class IEnumerableM implements Monad<IEnumerable> {
  IEnumerable<T> pure(T v) {
    return (new List<T>(){v}).AsReadOnly();
  }

  IEnumerable<B> bind(IEnumerable<A> mv, Func<A, IEnumerable<B>> f) {
    ;; equivalent to mv.SelectMany(f)
    return (from a in mv
            from b in f(a)
            select b);
  }
}

(Kasıtlı olarak LINQ sözdizimi ve monads arasındaki ilişkiyi çağırmak için LINQ sözdizimini kullanıyorum. Ancak LINQ sorgusunu bir çağrı ile değiştirebileceğimizi unutmayın SelectMany.)

Şimdi, bir monad tanımlayabilir miyiz IObservable? Öyle görünüyor:

class IObservableM implements Monad<IObservable> {
  IObservable<T> pure(T v){
    Observable.Return(v);
  }

  IObservable<B> bind(IObservable<A> mv, Func<A, IObservable<B>> f){
    mv.SelectMany(f);
  }
}

Bir monadımız olduğundan emin olmak için, monad yasalarını kanıtlamamız gerekir. Bu önemsiz olmayabilir (ve yalnızca spesifikasyondan kanıtlanıp kanıtlanamayacaklarını bilmek için Rx.NET'e yeterince aşina değilim), ancak umut verici bir başlangıç. Bu tartışmanın geri kalanını kolaylaştırmak için, sadece bu durumda monad yasalarının geçerli olduğunu varsayalım.

Ücretsiz Monads

Tekil "serbest monad" yoktur. Daha ziyade, serbest monadlar functorlardan inşa edilen bir monad sınıfıdır. Yani, bir functor verildiğinde F, otomatik olarak bir monad F(yani serbest monad F) türetebiliriz .

functors

Monadlar gibi, functors aşağıdaki üç öğe ile tanımlanabilir:

  • Kısıtlanmamış tek bir değişken üzerinde parametreleştirilmiş bir veri türü.
  • İki işlem:

    • puresaf bir değeri işleve sarar. Bu purebir monad için benzerdir . Aslında, aynı zamanda bir monad olan functors için, ikisi aynı olmalıdır.
    • fmapgirişteki değerleri, verilen bir işlev aracılığıyla çıkıştaki yeni değerlerle eşleştirir. İmzası:

      F<B> fmap(Func<A, B> f, F<A> fv)
      

Monadlar gibi, functorların da functor yasalarına uymaları gerekir.

Monad'lara benzer şekilde, functors'ı aşağıdaki arayüz üzerinden modelleyebiliriz:

interface Functor<F> {
  F<T> pure(T v);
  F<B> fmap(Func<A, B> f, F<A> fv);
}

Şimdi, monadlar functorların bir alt sınıfı olduğu için, Monadbiraz da yeniden düzenleyebiliriz :

interface Monad<M> extends Functor<M> {
  M<T> join(M<M<T>> mmv) {
    Func<T, T> identity = (x => x);
    return mmv.bind(x => x); // identity function
  }

  M<B> bind(M<A> mv, Func<A, M<B>> f) {
    join(fmap(f, mv));
  }
}

Burada ek bir yöntem ekledim joinve her ikisinin joinve varsayılan uygulamalarını sağladım bind. Ancak bunların dairesel tanımlamalar olduğuna dikkat edin. Bu yüzden en az birini veya diğerini geçersiz kılmanız gerekir. Ayrıca, pureşimdi miras alındığını unutmayın Functor.

IObservable ve Özgür Monadlar

Şimdi, bir monad tanımladığımız için IObservableve monadlar bir functor alt sınıfı olduğundan, bunun için bir functor örneği tanımlayabilmemiz gerekir IObservable. İşte bir tanım:

class IObservableF implements Functor<IObservable> {
  IObservable<T> pure(T v) {
    return Observable.Return(v);
  }

  IObservable<B> fmap(Func<A, B> f, IObservable<A> fv){
    return fv.Select(f);
  }
}

Şimdi için tanımlanmış bir işleve sahip IObservableolduğumuza göre, bu işleçten ücretsiz bir monad oluşturabiliriz. IObservableSerbest monadlarla tam olarak bu şekilde ilişkilidir - yani, ücretsiz bir monad inşa edebiliriz IObservable.


Kategori teorisinin anlayışlı anlayışı! Nasıl yaratıldıklarından bahsetmeyen bir şeyden sonra, fonksiyonel bir mimari oluştururken ve bunlardan biriyle efekt kompozisyonu modellemedeki farklılıklar hakkında daha fazla şey yaptım. FreeMonad, birleştirilmiş işlemler için DSL oluşturmak için kullanılabilirken, IObservables zaman içindeki ayrık değerlerle ilgilidir.
MLProgrammer-CiM

1
@ MLProgrammer-CiM, önümüzdeki birkaç gün içinde bununla ilgili bazı bilgiler ekleyip ekleyemeyeceğimi göreceğim.
Nathan Davis

Ücretsiz
monad'ların
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.