我正在努力初步了解单子.
我有一个数据层调用,其结果我想要返回一个结果,例如没有行更新/数据集等,或异常.我想我需要使用我可以看到的异常单子,作为一个单体的特殊情况
我已经看了各种各样的样本 – 吨的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>()方法负责区分以前执行的方法是否引发了异常.