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.
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 .