Я пытаюсь получить более глубокое понимание монад.Поэтому я начал немного копаться в Может быть, Монаду.
Есть одна вещь, которую я, кажется, просто не понимаю.Прочитайте это:
"Таким образом, возможное связывание приводит к короткому замыканию. Если в какой-либо из цепочек операций какая-либо из них вернет Nothing, оценка прекратится и ничего не будет возвращено из всей цепочки."
From: 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; }
}
Понятно, что мы пытаемся копнуть глубже в дерево узлов, чемвозможный.Тем не менее, я не вижу, как он действует в соответствии с цитатами, которые я упомянул.Я имею в виду, конечно, я учел нулевые проверки, и пример работает.Тем не менее, это не разорвать цепь рано.Если вы установите точки останова, вы увидите, что каждая операция 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 действительно ломается на уровне, где первое значение равно нулю.
Кто-нибудь может мне помочь, чтобы получить это ясно?
UPDATE
Как указал Патрик, да, это правда, что каждая привязка будет задействована, даже если у нас будет только 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();
});