Bla*_*son 8 c# monads functional-programming monad-transformers higher-kinded-types
我正在使用C#中的monad变换器.
我想知道我提供的以下代码是否表明我已理解这一点.
我对此很新,所以任何反馈/评论都非常受欢迎.
此示例仅用于在验证monad中包装monad monad.
using System;
using NUnit.Framework;
namespace Monads
{
public static class MaybeExtensions
{
public static IMaybe<T> ToMaybe<T>(this T value)
{
if (value == null)
return new None<T>();
return new Just<T>(value);
}
}
public interface IMaybe<T>
{
IMaybe<U> Select<U>(Func<T, U> f);
IMaybe<U> SelectMany<U>(Func<T, IMaybe<U>> f);
U Fold<U>(Func<U> error, Func<T, U> success);
}
public class Just<T> : IMaybe<T>
{
public Just(T value)
{
this.value = value;
}
public IMaybe<U> Select<U>(Func<T, U> f)
{
return f(value).ToMaybe();
}
public IMaybe<U> SelectMany<U>(Func<T, IMaybe<U>> f)
{
return f(value);
}
public U Fold<U>(Func<U> error, Func<T, U> success)
{
return success(value);
}
public IValidation<U, T> ToValidationT<U>()
{
return new ValidationMaybeT<U, T>(this, default(U));
}
private readonly T value;
}
public class None<T> : IMaybe<T>
{
public IMaybe<U> Select<U>(Func<T, U> f)
{
return new None<U>();
}
public IMaybe<U> SelectMany<U>(Func<T, IMaybe<U>> f)
{
return new None<U>();
}
public U Fold<U>(Func<U> error, Func<T, U> success)
{
return error();
}
public IValidation<U, T> ToValidationT<U>(U exceptionalValue)
{
return new ValidationMaybeT<U, T>(this, exceptionalValue);
}
}
public class Customer
{
public Customer(string name)
{
Name = name;
}
public string Name { get; set; }
}
public interface IValidation<T, U>
{
IValidation<T, V> Select<V>(Func<U, V> f);
IValidation<T, V> SelectMany<V>(Func<U, IValidation<T, V>> f);
}
public class ValidationError<T, U> : IValidation<T, U>
{
public ValidationError(T error)
{
Error = error;
}
public IValidation<T, V> Select<V>(Func<U, V> f)
{
return new ValidationError<T, V>(Error);
}
public IValidation<T, V> SelectMany<V>(Func<U, IValidation<T, V>> f)
{
return new ValidationError<T, V>(Error);
}
public T Error { get; private set; }
}
public class ValidationSuccess<T, U> : IValidation<T, U>
{
public ValidationSuccess(U value)
{
Result = value;
}
public IValidation<T, V> Select<V>(Func<U, V> f)
{
return new ValidationSuccess<T, V>(f(Result));
}
public IValidation<T, V> SelectMany<V>(Func<U, IValidation<T, V>> f)
{
return f(Result);
}
public U Result { get; private set; }
}
public class ValidationMaybeT<T, U> : IValidation<T, U>
{
public ValidationMaybeT(IMaybe<U> value, T error)
{
Value = value;
Error = error;
}
public IValidation<T, V> Select<V>(Func<U, V> f)
{
return Value.Fold<IValidation<T, V>>(() => new ValidationError<T, V>(Error), s => new ValidationSuccess<T, V>(f(s)));
}
ValidationError<T, V> SelectManyError<V>()
{
return new ValidationError<T, V>(Error);
}
public IValidation<T, V> SelectMany<V>(Func<U, IValidation<T, V>> f)
{
return Value.Fold(() => SelectManyError<V>(), s => f(s));
}
public IMaybe<U> Value { get; private set; }
public T Error { get; private set; }
}
public interface ICustomerRepository
{
IValidation<Exception, Customer> GetById(int id);
}
public class CustomerRepository : ICustomerRepository
{
public IValidation<Exception, Customer> GetById(int id)
{
if (id < 0)
return new None<Customer>().ToValidationT<Exception>(new Exception("Customer Id less than zero"));
return new Just<Customer>(new Customer("Structerre")).ToValidationT<Exception>();
}
}
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<Exception, Customer> LogResult(Customer c)
{
Console.WriteLine("Deleting: " + c.Name);
return new ValidationSuccess<Exception, Customer>(c);
//return new ValidationError<Exception, Customer>(new Exception("Unable write log"));
}
private IValidation<Exception, Customer> SendEmail(Customer c)
{
Console.WriteLine("Emailing: " + c.Name);
return new ValidationSuccess<Exception, Customer>(c);
}
ICustomerRepository customerRepository;
}
[TestFixture]
public class MonadTests
{
[Test]
public void Testing_With_Maybe_Monad()
{
new CustomerService(new CustomerRepository()).Delete(-1);
}
}
}
Run Code Online (Sandbox Code Playgroud)
另一个较小的子问题是,如果C#具有更高的kinded类型,我可以只实现一次这个类(ValidationT)并且它适用于所有其他包装的monad或者这是不正确的?
几乎是最快的答案。您ValidationMaybeT正在存储的值Maybe,而真正的monad转换器将具有Maybe和和Validationmonad 的行为,并且可以根据需要修改包装的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
Run Code Online (Sandbox Code Playgroud)
这些函数在函数式编程中最有用,并且几乎可以让您执行所需的任何操作。
因此,在所有实现这些标准功能的monad上,它们都变成了更高种类的类型。并不是编译器知道这一点,它们都只是同一“集合”的一部分。
然后,我编写了一个T4模板,以生成变压器函数作为扩展方法(它们带有T后缀),用于“高级类型”中monad和函数的每种组合。
因此,例如:
var list = List(Some(1),None,Some(2),None,Some(3));
var total = list.SumT();
Run Code Online (Sandbox Code Playgroud)
上面的代码导致6。的定义SumT是:
int SumT(Lst<Option<int>> self) =>
self.Map( s => s.Sum() ).Sum();
Run Code Online (Sandbox Code Playgroud)
FilterT 例如,也可以在内部monad上工作:
var list = List(Some(1),None,Some(2),None,Some(3));
list = list.FilterT(x => x > 2);
Run Code Online (Sandbox Code Playgroud)
因此,扩展方法路由是一种非常好的方法。代替创建新类型,使用:
IValidation<IMaybe<T>>
Run Code Online (Sandbox Code Playgroud)
然后提供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>>。原始实现在这里:
无论如何,我希望能有所帮助!
| 归档时间: |
|
| 查看次数: |
1324 次 |
| 最近记录: |