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:
return
Saya pure
mengambil nilai "murni" (yaitu T
nilai) dan "membungkusnya" ke dalam monad (yaitu, menghasilkan M<T>
nilai). Karena return
kata kunci dicadangkan dalam C #, saya akan gunakan pure
untuk merujuk ke operasi ini mulai sekarang. Dalam C #, pure
akan menjadi metode dengan tanda tangan seperti:
M<T> pure(T v);
bind
/ flatmap
mengambil nilai monadik ( M<A>
) dan fungsi f
. f
mengambil nilai murni dan mengembalikan nilai monadik ( M<B>
). Dari ini, bind
menghasilkan nilai monadik baru ( M<B>
). bind
memiliki tanda tangan C # berikut:
M<B> bind(M<A> mv, Func<A, M<B>> f);
Juga, untuk menjadi seorang monad, pure
dan bind
diharuskan 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:
pure
membungkus nilai murni ke dalam functor. Ini analog dengan pure
untuk monad. Bahkan, untuk functors yang juga merupakan monad, keduanya harus identik.
fmap
memetakan 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 Monad
sedikit 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 join
dan bind
. Perhatikan, bagaimanapun, bahwa ini adalah definisi melingkar. Jadi, Anda harus menimpa setidaknya satu atau yang lain. Juga, perhatikan bahwa pure
sekarang diwarisi dari Functor
.
IObservable
dan Monads Gratis
Sekarang, karena kita telah mendefinisikan sebuah monad untuk IObservable
dan 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 IObservable
hubungannya dengan monad gratis - yaitu, bahwa kita dapat membangun monad gratis dari IObservable
.
Cont
satu-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 .