モナドをより深く理解しようとしています。そのため、私は Maybe モナドを少し掘り下げ始めました。
私が正しくないように見えることが1つあります。これを読む:
「したがって、Maybe Bind は短絡回路として機能します。操作のチェーンで、いずれかが Nothing を返す場合、評価は中止され、チェーン全体から Nothing が返されます。」
から: http://mikehadlow.blogspot.com/2011/01/monads-in-c-5-maybe.html
この:
「Maybe<T>
タイプの場合、バインディングは単純なルールに従って実装されます。チェーンがある時点で空の値を返す場合、チェーンのそれ以降のステップは無視され、代わりに空の値が返されます」
From: 「C# での関数型プログラミング」http://www.amazon.com/Functional-Programming-Techniques-Projects-Programmer/dp/0470744588/
では、コードを見てみましょう。ここに私の多分モナドがあります:
public class Maybe<T>
{
public static readonly Maybe<T> Empty = new Maybe<T>();
public Maybe(T value)
{
Value = value;
}
private Maybe()
{
}
public bool HasValue()
{
return !EqualityComparer<T>.Default.Equals(Value, default(T));
}
public T Value { get; private set; }
public Maybe<R> Bind<R>(Func<T, Maybe<R>> apply)
{
return HasValue() ? apply(Value) : Maybe<R>.Empty;
}
}
public static class MaybeExtensions
{
public static Maybe<T> ToMaybe<T>(this T value)
{
return new Maybe<T>(value);
}
}
そして、これがモナドを使った私のサンプルコードです:
class Program
{
static void Main(string[] args)
{
var node = new Node("1", new Node("2", new Node("3", new Node("4", null))));
var childNode = node.ChildNode
.ToMaybe()
.Bind(x => x.ChildNode.ToMaybe())
.Bind(x => x.ChildNode.ToMaybe())
.Bind(x => x.ChildNode.ToMaybe())
.Bind(x => x.ChildNode.ToMaybe())
.Bind(x => x.ChildNode.ToMaybe());
Console.WriteLine(childNode.HasValue() ? childNode.Value.Value : "");
Console.ReadLine();
}
}
public class Node
{
public Node(string value, Node childNode)
{
Value = value;
ChildNode = childNode;
}
public string Value { get; set; }
public Node ChildNode { get; private set; }
}
ノード ツリーを可能な限り深く掘り下げようとしていることは明らかです。しかし、私が言及した引用によると、それがどのように機能しているかはわかりません。つまり、もちろん、null チェックを除外して、例が機能することを意味します。ただし、チェーンを早期に切断することはありません。Bind()
ブレークポイントを設定すると、最後の操作の値なしですべての操作が使用されることがわかります。しかし、20 レベル深く掘り下げて、実際には 3 レベルしか下がらない場合でも、20 レベルをチェックするのでしょうか、それとも間違っているのでしょうか?
これを非モナドアプローチと比較してください:
if (node.ChildNode != null
&& node.ChildNode.ChildNode != null
&& node.ChildNode.ChildNode.ChildNode != null)
{
Console.WriteLine(node.ChildNode.ChildNode.ChildNode.Value);
}
これこそがショートサーキットと言うべきものではないでしょうか。この場合、if は最初の値が null であるレベルで実際に中断するためです。
これを明確にするのを手伝ってくれる人はいますか?
アップデート
Patrik が指摘したように、3 つのレベルしかなく、20 レベルの深さにしようとしても、各バインドが呼び出されるのは事実です。ただし、Bind() 呼び出しに提供される実際の式は評価されません。この例を編集して、効果を明確にすることができます。
var childNode = node.ChildNode
.ToMaybe()
.Bind(x =>
{
Console.WriteLine("We will see this");
return x.ChildNode.ToMaybe();
})
.Bind(x => x.ChildNode.ToMaybe())
.Bind(x => x.ChildNode.ToMaybe())
.Bind(x => x.ChildNode.ToMaybe())
.Bind(x =>
{
Console.WriteLine("We won't see this");
return x.ChildNode.ToMaybe();
});