我正在努力初步了解单子.
我有一个数据层调用,其结果我想要返回一个结果,例如没有行更新/数据集等,或异常.我想我需要使用我可以看到的异常单子,作为一个单体的特殊情况
我已经看了各种各样的样本 – 吨的Maybe样本,我不太确定如何或如果推广这个成为一个单体 – 但我找不到哪些不在haskell – 不幸的是,我最肯定不要grok haskell!
我想知道有没有人可以指点我的样品.
解决方法
在C#中学习一点关于monads的同时,为了练习,我为自己实现了一个特殊的monad.有了这个monad,你可以链接可能会抛出异常的操作,就像这两个例子一样:
var exc1 = from x in 0.ToExceptional() from y in Exceptional.Execute(() => 6 / x) from z in 7.ToExceptional() select x + y + z; Console.WriteLine("Exceptional Result 1: " + exc1); var exc2 = Exceptional.From(0) .ThenExecute(x => x + 6 / x) .ThenExecute(y => y + 7); Console.WriteLine("Exceptional Result 2: " + exc2);
两个表达式产生相同的结果,只是语法不同.结果将是一个特殊的< T>与出现的DivideByZeroException设置为属性.第一个例子显示了使用LINQ的monad的“核心”,第二个例子包含一个不同的,也许更可读的语法,它以更容易理解的方式说明了方法链接.
那么它是如何实现的呢?这里是超凡的< T>类型:
public class Exceptional<T> { public bool HasException { get; private set; } public Exception Exception { get; private set; } public T Value { get; private set; } public Exceptional(T value) { HasException = false; Value = value; } public Exceptional(Exception exception) { HasException = true; Exception = exception; } public Exceptional(Func<T> getValue) { try { Value = getValue(); HasException = false; } catch (Exception exc) { Exception = exc; HasException = true; } } public override string ToString() { return (this.HasException ? Exception.GetType().Name : ((Value != null) ? Value.ToString() : "null")); } }
Monad通过扩展方法ToExceptional< T>()和SelectMany< T,U>()完成,对应于monad的Unit和Bind函数:
public static class ExceptionalMonadExtensions { public static Exceptional<T> ToExceptional<T>(this T value) { return new Exceptional<T>(value); } public static Exceptional<T> ToExceptional<T>(this Func<T> getValue) { return new Exceptional<T>(getValue); } public static Exceptional<U> SelectMany<T,U>(this Exceptional<T> value,Func<T,Exceptional<U>> k) { return (value.HasException) ? new Exceptional<U>(value.Exception) : k(value.Value); } public static Exceptional<V> SelectMany<T,U,V>(this Exceptional<T> value,Exceptional<U>> k,V> m) { return value.SelectMany(t => k(t).SelectMany(u => m(t,u).ToExceptional())); } }
还有一些小帮手,不是monad的核心部分:
public static class Exceptional { public static Exceptional<T> From<T>(T value) { return value.ToExceptional(); } public static Exceptional<T> Execute<T>(Func<T> getValue) { return getValue.ToExceptional(); } } public static class ExceptionalExtensions { public static Exceptional<U> ThenExecute<T,U> getValue) { return value.SelectMany(x => Exceptional.Execute(() => getValue(x))); } }
一些解释:只要链中的一种方法抛出异常,就会执行一个使用这个monad构建的方法链.在这种情况下,不再执行链的更多方法,并且将返回第一个抛出的异常作为异常< T>的一部分.结果.在这种情况下,将会设置HasException和Exception属性.如果没有发生异常,则HasException将为false,并且将设置Value属性,其中包含执行的方法链的结果.
请注意,异常< T>(Func< T> getValue)构造器负责异常处理,并且SelectMany< T,U>()方法负责区分以前执行的方法是否引发了异常.