我正在使用C#中的monad变换器.
我想知道我提供的以下代码是否表明我已理解这一点.
我对此很新,所以任何反馈/评论都非常受欢迎.
此示例仅用于在验证monad中包装monad monad.
using System; using NUnit.Framework; namespace Monads { public static class MaybeExtensions { public static IMaybeToMaybe (this T value) { if (value == null) return new None (); return new Just (value); } } public interface IMaybe { IMaybe Select(Func f); IMaybe SelectMany(Func > f); U Fold(Func error, Func success); } public class Just : IMaybe { public Just(T value) { this.value = value; } public IMaybe Select(Func f) { return f(value).ToMaybe(); } public IMaybe SelectMany(Func > f) { return f(value); } public U Fold(Func error, Func success) { return success(value); } public IValidation ToValidationT() { return new ValidationMaybeT(this, default(U)); } private readonly T value; } public class None : IMaybe { public IMaybe Select(Func f) { return new None(); } public IMaybe SelectMany(Func > f) { return new None(); } public U Fold(Func error, Func success) { return error(); } public IValidation ToValidationT(U exceptionalValue) { return new ValidationMaybeT(this, exceptionalValue); } } public class Customer { public Customer(string name) { Name = name; } public string Name { get; set; } } public interface IValidation { IValidation Select (Func f); IValidation SelectMany (Func> f); } public class ValidationError : IValidation { public ValidationError(T error) { Error = error; } public IValidation Select (Func f) { return new ValidationError (Error); } public IValidation SelectMany (Func> f) { return new ValidationError (Error); } public T Error { get; private set; } } public class ValidationSuccess : IValidation { public ValidationSuccess(U value) { Result = value; } public IValidation Select (Func f) { return new ValidationSuccess (f(Result)); } public IValidation SelectMany (Func> f) { return f(Result); } public U Result { get; private set; } } public class ValidationMaybeT : IValidation { public ValidationMaybeT(IMaybe value, T error) { Value = value; Error = error; } public IValidation Select (Func f) { return Value.Fold >(() => new ValidationError (Error), s => new ValidationSuccess (f(s))); } ValidationError SelectManyError () { return new ValidationError (Error); } public IValidation SelectMany (Func> f) { return Value.Fold(() => SelectManyError (), s => f(s)); } public IMaybe Value { get; private set; } public T Error { get; private set; } } public interface ICustomerRepository { IValidation GetById(int id); } public class CustomerRepository : ICustomerRepository { public IValidation GetById(int id) { if (id < 0) return new None ().ToValidationT (new Exception("Customer Id less than zero")); return new Just (new Customer("Structerre")).ToValidationT (); } } public interface ICustomerService { void Delete(int id); } public class CustomerService : ICustomerService { public CustomerService(ICustomerRepository customerRepository) { this.customerRepository = customerRepository; } public void Delete(int id) { customerRepository.GetById(id) .SelectMany(x => SendEmail(x).SelectMany(y => LogResult(y))); } public IValidation LogResult(Customer c) { Console.WriteLine("Deleting: " + c.Name); return new ValidationSuccess (c); //return new ValidationError (new Exception("Unable write log")); } private IValidation SendEmail(Customer c) { Console.WriteLine("Emailing: " + c.Name); return new ValidationSuccess (c); } ICustomerRepository customerRepository; } [TestFixture] public class MonadTests { [Test] public void Testing_With_Maybe_Monad() { new CustomerService(new CustomerRepository()).Delete(-1); } } }
另一个较小的子问题是,如果C#具有更高的kinded类型,我可以只实现一次这个类(ValidationT)并且它适用于所有其他包装的monad或者这是不正确的?
几乎是最快的答案。您ValidationMaybeT
正在存储的值Maybe
,而真正的monad转换器将具有Maybe
和和Validation
monad 的行为,并且可以根据需要修改包装的monad的默认行为。
这是一种非常手动的方式,我不一定会推荐这样做,它很快就会变得非常混乱。C#缺乏更高种类的多态性会抓住每一个机会。
我管理的最接近(即使不是适当的monad转换器系统)也位于我的库中:Language-Ext
项目中有13个monad(Option,Map,Lst,Either,Try,Reader等),我为所有这些实现了一组标准功能:
Sum Count Bind Exists Filter Fold ForAll Iter Map Select SeletMany Where Lift
这些函数在函数式编程中最有用,并且几乎可以让您执行所需的任何操作。
因此,在所有实现这些标准功能的monad上,它们都变成了更高种类的类型。并不是编译器知道这一点,它们都只是同一“集合”的一部分。
然后,我编写了一个T4模板,以生成变压器函数作为扩展方法(它们带有T
后缀),用于“高级类型”中monad和函数的每种组合。
因此,例如:
var list = List(Some(1),None,Some(2),None,Some(3)); var total = list.SumT();
上面的代码导致6
。的定义SumT
是:
int SumT(Lst<Option<int>> self) => self.Map( s => s.Sum() ).Sum();
FilterT
例如,也可以在内部monad上工作:
var list = List(Some(1),None,Some(2),None,Some(3)); list = list.FilterT(x => x > 2);
因此,扩展方法路由是一种非常好的方法。代替创建新类型,使用:
IValidation<IMaybe<T>>
然后提供Maybe
扩展方法IValidation<IMaybe<T>>
您可以执行我的操作并从标准集中自动生成,也可以手动编写。然后,它可以使您Maybe
和Validation
实施保持整洁,并保留定制的转换器功能。
如果您有兴趣,这是我用来生成转换方法的T4模板(说实话,这真是摇摇欲坠):LanguageExt.Core / HKT.tt
这是生成的代码: LanguageExt.Core / HKT.cs
在进行上述HKT之前,我用与您尝试的方法类似的方法,我有一个monad叫做TryOption<T>
a Try
和an Option
。但是现在我可以用HKT的新东西写东西了Try<Option<T>>
。原始实现在这里:
无论如何,我希望能有所帮助!