Bagaimana cara korelasi Free monad dan Extensions?

14

Saya berasal dari latar belakang C #, di mana LINQ berevolusi menjadi Rx.NET, tetapi selalu memiliki minat pada FP. Setelah beberapa pengantar untuk monad dan beberapa proyek sampingan di F #, saya siap untuk mencoba dan melangkah ke tingkat berikutnya.

Sekarang, setelah beberapa pembicaraan tentang monad gratis dari orang-orang dari Scala, dan beberapa artikel di Haskell, atau F #, saya telah menemukan tata bahasa dengan penerjemah untuk pemahaman yang sangat mirip dengan IObservablerantai.

Di FRP Anda membuat definisi operasi dari potongan spesifik domain yang lebih kecil termasuk efek samping dan kegagalan yang tetap ada di dalam rantai, dan memodelkan aplikasi Anda sebagai serangkaian operasi dan efek samping. Dalam monad gratis, jika saya mengerti dengan benar, Anda melakukan hal yang sama dengan menjadikan operasi Anda sebagai functors, dan mengangkatnya menggunakan coyoneda.

Apa perbedaan antara keduanya yang memiringkan jarum ke salah satu pendekatan? Apa perbedaan mendasar ketika mendefinisikan layanan atau program Anda?

MLProgrammer-CiM
sumber
2
Berikut ini cara menarik untuk memikirkan masalah ... FRP dapat dilihat sebagai monad, meskipun biasanya tidak dirumuskan seperti itu . Sebagian besar (walaupun tidak semua) monad bersifat isomorfis terhadap monad bebas . Seperti Contsatu-satunya monad yang saya lihat menyarankan yang tidak dapat diekspresikan melalui monad gratis, orang mungkin dapat berasumsi bahwa FRP bisa. Seperti bisa hampir hal lain .
Jules
2
Menurut Erik Meijer, perancang LINQ dan Rx.NET, IObservableadalah turunan dari kelanjutan monad.
Jörg W Mittag
1
Saya tidak punya waktu untuk mengerjakan perinciannya sekarang, tetapi dugaan saya adalah bahwa baik ekstensi RX dan pendekatan monad gratis mencapai tujuan yang sangat mirip tetapi mungkin memiliki struktur yang sedikit berbeda. Ada kemungkinan bahwa RX Observables adalah monad sendiri dan kemudian Anda dapat memetakan perhitungan monad gratis untuk yang menggunakan observable — itu kira-kira seperti apa arti "bebas" dalam "monad bebas". Atau mungkin hubungannya tidak langsung dan Anda hanya mengetahui bagaimana mereka digunakan untuk tujuan yang sama.
Tikhon Jelvis

Jawaban:

6

Monad

Sebuah monad terdiri dari

  • Seorang petugas akhir . Dalam dunia rekayasa perangkat lunak kami, kami dapat mengatakan ini terkait dengan tipe data dengan parameter tipe tunggal yang tidak dibatasi. Dalam C #, ini akan menjadi sesuatu dari bentuk:

    class M<T> { ... }
    
  • Dua operasi yang didefinisikan pada tipe data itu:

    • returnSaya puremengambil nilai "murni" (yaitu Tnilai) dan "membungkusnya" ke dalam monad (yaitu, menghasilkan M<T>nilai). Karena returnkata kunci dicadangkan dalam C #, saya akan gunakan pureuntuk merujuk ke operasi ini mulai sekarang. Dalam C #, pureakan menjadi metode dengan tanda tangan seperti:

      M<T> pure(T v);
      
    • bind/ flatmapmengambil nilai monadik ( M<A>) dan fungsi f. fmengambil nilai murni dan mengembalikan nilai monadik ( M<B>). Dari ini, bindmenghasilkan nilai monadik baru ( M<B>). bindmemiliki tanda tangan C # berikut:

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

Juga, untuk menjadi seorang monad, puredan binddiharuskan untuk mematuhi tiga hukum monad.

Sekarang, salah satu cara untuk memodelkan monads di C # adalah dengan membangun antarmuka:

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

(Catatan: Agar semuanya singkat dan ekspresif, saya akan mengambil beberapa kebebasan dengan kode di seluruh jawaban ini.)

Sekarang kita dapat mengimplementasikan monad untuk datagypes konkret dengan mengimplementasikan implementasi konkret dari Monad<M>. Misalnya, kami dapat menerapkan monad berikut untuk 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);
  }
}

(Saya sengaja menggunakan sintaks LINQ untuk memanggil hubungan antara sintaks LINQ dan monad. Tetapi perhatikan kita bisa mengganti kueri LINQ dengan panggilan ke SelectMany.)

Sekarang, bisakah kita mendefinisikan monad IObservable? Tampaknya begitu:

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

Untuk memastikan kita memiliki monad, kita perlu membuktikan hukum monad. Ini bisa non-sepele (dan saya tidak cukup akrab dengan Rx.NET untuk mengetahui apakah mereka bahkan dapat dibuktikan dari spesifikasinya saja), tetapi ini merupakan awal yang menjanjikan. Untuk memudahkan diskusi ini, mari kita asumsikan hukum monad berlaku dalam kasus ini.

Monads gratis

Tidak ada "monad gratis" tunggal. Sebaliknya, monad gratis adalah kelas monad yang dibangun dari functors. Artinya, diberi fungsi F, kita dapat secara otomatis memperoleh monad untuk F(yaitu, monad gratis F).

Functors

Seperti Monads, functors dapat didefinisikan oleh tiga item berikut:

  • Tipe data, diparameterisasi menjadi satu, jenis variabel tidak dibatasi.
  • Dua operasi:

    • puremembungkus nilai murni ke dalam functor. Ini analog dengan pureuntuk monad. Bahkan, untuk functors yang juga merupakan monad, keduanya harus identik.
    • fmapmemetakan nilai dalam input ke nilai baru dalam output melalui fungsi yang diberikan. Tanda tangan itu adalah:

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

Seperti halnya monad, functors diharuskan untuk mematuhi hukum functor.

Mirip dengan monads, kita dapat memodelkan functors melalui antarmuka berikut:

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

Sekarang, karena monad adalah sub-kelas dari fungsi, kita juga bisa Monadsedikit refactor :

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

Di sini saya telah menambahkan metode tambahan join,, dan memberikan implementasi standar keduanya joindan bind. Perhatikan, bagaimanapun, bahwa ini adalah definisi melingkar. Jadi, Anda harus menimpa setidaknya satu atau yang lain. Juga, perhatikan bahwa puresekarang diwarisi dari Functor.

IObservable dan Monads Gratis

Sekarang, karena kita telah mendefinisikan sebuah monad untuk IObservabledan karena monad adalah sub-kelas dari fungsi, maka kita harus dapat mendefinisikan instance dari functor untuk IObservable. Inilah satu definisi:

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

Sekarang kita memiliki functor yang ditentukan IObservable, kita dapat membangun monad gratis dari functor itu. Dan itulah tepatnya bagaimana IObservablehubungannya dengan monad gratis - yaitu, bahwa kita dapat membangun monad gratis dari IObservable.

Nathan Davis
sumber
Pemahaman mendalam tentang teori kategori! Saya mencari sesuatu yang berbicara bukan tentang bagaimana mereka dibuat, lebih tentang perbedaan ketika membangun arsitektur fungsional dan komposisi efek pemodelan dengan salah satu dari mereka. FreeMonad dapat digunakan untuk membangun DSL untuk operasi yang direvisi, sedangkan IObservables lebih tentang nilai diskrit dari waktu ke waktu.
MLProgrammer-CiM
1
@ MLProgrammer-CiM, saya akan melihat apakah saya dapat menambahkan beberapa wawasan tentang itu dalam beberapa hari mendatang.
Nathan Davis
Saya suka contoh praktis dari monad gratis
l