我正在尝试更深入地了解 Monads。因此,我开始深入研究 Maybe Monad。
有一件事我似乎没有做对。读这个:
“因此,Maybe Bind 起到了短路的作用。在任何一个操作链中,如果其中任何一个返回 Nothing,则评估将停止,整个链将返回 Nothing。”
来自: http: //mikehadlow.blogspot.com/2011/01/monads-in-c-5-maybe.html
还有这个:
“对于Maybe<T>
类型,绑定是根据简单的规则实现的:如果链在某个点返回一个空值,则链中的进一步步骤将被忽略,而是返回一个空值”
来自:“C# 中的函数式编程” http://www.amazon.com/Functional-Programming-Techniques-Projects-Programmer/dp/0470744588/
好的,让我们看一下代码。这是我的Maybe Monad:
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);
}
}
这是我使用 monad 的示例代码:
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; }
}
很明显,我们正在尝试比可能更深入地挖掘节点树。但是,根据我提到的报价,我看不到它是如何运作的。我的意思是,我当然已经排除了空检查并且示例有效。然而,它并没有提前打破这个链条。如果设置断点,您将看到每个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();
});