Bu soruyu yayınlamamdan bu yana bir yıl geçti. Gönderdikten sonra Haskell'i birkaç aylığına araştırdım. Çok keyif aldım, ama Monad'ları araştırmaya hazır olduğum için kenara koydum. İşe geri döndüm ve projemin gerektirdiği teknolojilere odaklandım.
Bu çok havalı. Yine de biraz soyut. Gerçek örneklerin yokluğu nedeniyle hangi monadların zaten karıştığını bilmeyen insanları hayal edebiliyorum.
Bu yüzden uymaya çalışmama izin verin ve gerçekten net olmak için, çirkin görünmesine rağmen C # 'da bir örnek yapacağım. Sonunda eşdeğer Haskell'i ekleyeceğim ve size IMO, monad'ların gerçekten işe yaramaya başladığı serin Haskell sözdizimsel şekeri göstereceğim.
Tamam, bu yüzden en kolay Monad'lardan birine Haskell'deki "Belki monad" denir. C # 'da Belki tipi çağrılır Nullable<T>. Temel olarak, geçerli ve bir değeri olan veya "boş" olan ve değeri olmayan bir değer kavramını içine alan küçük bir sınıftır.
Bu tipteki değerleri birleştirmek için bir monadın içine yapışmak için yararlı bir şey, başarısızlık kavramıdır. Yani, birden çok boş değerlere bakıp nullbunlardan herhangi biri boş olur olmaz geri dönmek istiyoruz . Örneğin, bir sözlükte veya başka bir şeyde çok sayıda anahtar ararsanız ve sonunda tüm sonuçları işlemek ve bunları bir şekilde birleştirmek istiyorsanız, ancak anahtarlardan herhangi biri sözlükte değilse, nullher şey için geri dönmek istiyorsun . Her aramayı manuel olarak kontrol etmek nullve geri dönmek zorunda kalmak sıkıcı olacaktır
, bu nedenle bu kontrolü bağlama operatörünün içinde gizleyebiliriz (bu bir tür monadların noktasıdır, kod operatörünü daha kolay hale getiren bağlama operatörüne kitap tutmayı gizleriz ayrıntıları unutabileceğimiz için kullanın).
İşte her şeyi motive eden program ( Binddaha sonra tanımlayacağım
, bu sadece neden güzel olduğunu göstermek için).
class Program
{
static Nullable<int> f(){ return 4; }
static Nullable<int> g(){ return 7; }
static Nullable<int> h(){ return 9; }
static void Main(string[] args)
{
Nullable<int> z =
f().Bind( fval =>
g().Bind( gval =>
h().Bind( hval =>
new Nullable<int>( fval + gval + hval ))));
Console.WriteLine(
"z = {0}", z.HasValue ? z.Value.ToString() : "null" );
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
Şimdi, bir an için bunu zaten NullableC # için destek olduğunu göz ardı edin (nullable ints'ı birlikte ekleyebilir ve her ikisi de null olursa null alabilirsiniz). Diyelim ki böyle bir özellik yok ve sadece özel bir sihri olmayan kullanıcı tanımlı bir sınıf. Mesele şu ki, Bindbir değişkeni Nullabledeğerimizin içeriğine bağlamak için işlevi kullanabilir ve sonra garip bir şey olmadığını iddia edebiliriz ve bunları normal ints gibi kullanabiliriz ve birlikte ekleyebiliriz. Biz sonunda null sonucu sarın ve bu null ya (eğer herhangi boş olacak f, gya da hdöner null) veya toplanmasının sonucu olacak f, gvehbirlikte. (bu, bir veritabanındaki bir satırı LINQ'daki bir değişkene nasıl bağlayabileceğimize benzer ve Bindoperatörün değişkenin yalnızca geçerli satır değerlerinden geçirileceğinden emin olacağı bilgisiyle güvenlidir .
Buna oynamak ve herhangi değiştirebilir f, gve hreturn null etmek ve her şey return null göreceksiniz.
Bu nedenle açıkça, bağlayıcı operatörün bu kontrolü bizim için yapması ve bir null değerle karşılaşması durumunda null döndürmesini kurtarması ve aksi takdirde Nullableyapının içindeki değer boyunca lambda'ya geçmesi gerekir .
İşte Bindoperatör:
public static Nullable<B> Bind<A,B>( this Nullable<A> a, Func<A,Nullable<B>> f )
where B : struct
where A : struct
{
return a.HasValue ? f(a.Value) : null;
}
Buradaki türler videodaki gibidir. Bir sürer M a
( Nullable<A>bu durum için C # sözdizimi) ve bir işlev aiçin
M b( Func<A, Nullable<B>>C # sözdizimi) ve bir döner M b
( Nullable<B>).
Kod, null değerinin bir değer içerip içermediğini kontrol eder ve eğer öyleyse onu ayıklar ve işleve iletir, aksi takdirde null değerini döndürür. Bu, Bindoperatörün bizim için tüm boş kontrol mantığını işleyeceği anlamına gelir . Yalnızca çağırdığımız değer Bindnull değilse,
bu değer lambda işlevine "iletilir", aksi takdirde erken kurtarırız ve tüm ifade null olur. Bu bizim, biz sadece kullanmak bu boş denetimi davranışının tamamen özgür olmak monad kullanan yazma o kodu verir Bind(ve monadic değere içindeki değere bağlı bir değişken olsun fval,
gvalve hvalörnek kodda) ve onlara güvenli kullanabilirsiniz Bindonları geçmeden önce null olup olmadığını kontrol etmeye özen gösterecek bilgi .
Bir monad ile yapabileceğiniz şeylere başka örnekler de var. Örneğin, Bindoperatörün bir girdi karakter akışıyla ilgilenmesini sağlayabilir ve ayrıştırıcı birleştiricileri yazmak için kullanabilirsiniz. Her ayrıştırıcı birleştirici, daha sonra geri izleme, ayrıştırıcı hataları vb Bind. zor bitler. Daha sonra belki biri monad'a günlük ekler, ancak monad'ı kullanan kod değişmez, çünkü tüm sihir Bindoperatörün tanımında gerçekleşir, kodun geri kalanı değişmez.
Son olarak, Haskell'de aynı kodun uygulanması ( --
bir yorum satırına başlar).
-- Here's the data type, it's either nothing, or "Just" a value
-- this is in the standard library
data Maybe a = Nothing | Just a
-- The bind operator for Nothing
Nothing >>= f = Nothing
-- The bind operator for Just x
Just x >>= f = f x
-- the "unit", called "return"
return = Just
-- The sample code using the lambda syntax
-- that Brian showed
z = f >>= ( \fval ->
g >>= ( \gval ->
h >>= ( \hval -> return (fval+gval+hval ) ) ) )
-- The following is exactly the same as the three lines above
z2 = do
fval <- f
gval <- g
hval <- h
return (fval+gval+hval)
Gördüğünüz gibi dosonunda güzel notasyon düz zorunlu kod gibi görünmesini sağlar. Ve aslında bu tasarım gereğidir. Monadlar, tüm yararlı şeyleri zorunlu programlamada (değiştirilebilir durum, IO vb.) Kapsüllemek için kullanılabilir ve bu güzel zorunlu benzeri sözdizimini kullanarak kullanılabilir, ancak perdelerin arkasında, hepsi sadece monadlar ve bağlama operatörünün akıllı bir uygulamasıdır! Serin şey uygulayarak kendi monads uygulamak olmasıdır >>=ve return. Ve bunu yaparsanız, bu monadlar dogösterimi de kullanabilecektir , bu da sadece iki işlevi tanımlayarak kendi küçük dillerinizi yazabileceğiniz anlamına gelir!