我在尝试着 神交 初步了解monads.
我有一个数据层调用,其结果我想单独返回,例如没有行更新/数据集等,或异常.我想我需要使用Exception monad,我可以看作是Either monad的特例
我已经看了几个样本 - 可能样本吨,我不太确定如何或如果将其概括为成为一个Either monad - 但我找不到任何不在haskell中 - 并且,不幸的是,我肯定不会grok haskell!
我想知道是否有人能指出我的任何样品.
Mat*_*ias 15
在学习C#中的Exceptional
monad时,为了练习,我为自己实现了monad.使用这个monad,你可以Exception
在这两个例子中链接可能会产生类似的操作:
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);
Run Code Online (Sandbox Code Playgroud)
两个表达式产生相同的结果,只是语法不同.结果将是Exceptional<T>
出现的DivideByZeroException
属性.第一个示例使用LINQ显示monad的"核心",第二个示例包含不同且可能更具可读性的语法,这说明了以更易理解的方式链接的方法.
那么,它是如何实现的?这是Exceptional<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"));
}
}
Run Code Online (Sandbox Code Playgroud)
单子是通过扩展方法完成ToExceptional<T>()
和SelectMany<T, U>()
,对应于单子的单位和绑定功能:
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, Func<T, Exceptional<U>> k, Func<T, U, V> m)
{
return value.SelectMany(t => k(t).SelectMany(u => m(t, u).ToExceptional()));
}
}
Run Code Online (Sandbox Code Playgroud)
还有一些小辅助结构,它们不是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>(this Exceptional<T> value, Func<T, U> getValue)
{
return value.SelectMany(x => Exceptional.Execute(() => getValue(x)));
}
}
Run Code Online (Sandbox Code Playgroud)
一些解释:只要链的一个方法抛出异常,就会执行使用此monad构建的方法链.在这种情况下,不会执行链的更多方法,并且第一个抛出的异常将作为Exceptional<T>
结果的一部分返回.在这种情况下HasException
,Exception
将设置和属性.如果没有Exception
发生时,HasException
会false
与该Value
属性将被设置,包含执行方法链的结果.
请注意,Exceptional<T>(Func<T> getValue)
构造函数负责异常处理,并且该SelectMany<T,U>()
方法负责区分之前执行的方法是否抛出异常.
Mik*_*kov 14
我们Either
在C#解决方案中实现了数据结构,我们很高兴使用它.以下是此类实现的最简单版本:
public class Either<TL, TR>
{
private readonly TL left;
private readonly TR right;
private readonly bool isLeft;
public Either(TL left)
{
this.left = left;
this.isLeft = true;
}
public Either(TR right)
{
this.right = right;
this.isLeft = false;
}
public T Match<T>(Func<TL, T> leftFunc, Func<TR, T> rightFunc)
=> this.isLeft ? leftFunc(this.left) : rightFunc(this.right);
public static implicit operator Either<TL, TR>(TL left) => new Either<TL, TR>(left);
public static implicit operator Either<TL, TR>(TR right) => new Either<TL, TR>(right);
}
Run Code Online (Sandbox Code Playgroud)
(我们的代码有更多辅助方法,但它们是可选的)
要点是
Left
或Right
我还描述了我们如何使用这种Either类型进行数据验证.
值得注意的是,现在有可用的 C# 库包含以下实现Either
:
language-ext库可用于 .Net 4.5.1 和 .Net Standard 1.3
LaYumba库可用于 .Net Standard 1.6 和 .Net Core 1.1。
这两个库都有很好的文档记录,LaYumba 被用作 Manning 书籍Functional Programming in C# 的基础。