C#中的Monad变形金刚

 hello簞調_290 发布于 2023-02-13 11:01

我正在使用C#中的monad变换器.
我想知道我提供的以下代码是否表明我已理解这一点.
我对此很新,所以任何反馈/评论都非常受欢迎.
此示例仅用于在验证monad中包装monad monad.

using System;
using NUnit.Framework;

namespace Monads
{
    public static class MaybeExtensions
    {
        public static IMaybe ToMaybe(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或者这是不正确的?

1 个回答
  • 几乎是最快的答案。您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
    

    这些函数在函数式编程中最有用,并且几乎可以让您执行所需的任何操作。

    因此,在所有实现这些标准功能的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>>

    您可以执行我的操作并从标准集中自动生成,也可以手动编写。然后,它可以使您MaybeValidation实施保持整洁,并保留定制的转换器功能。

    如果您有兴趣,这是我用来生成转换方法的T4模板(说实话,这真是摇摇欲坠):LanguageExt.Core / HKT.tt

    这是生成的代码: LanguageExt.Core / HKT.cs

    在进行上述HKT之前,我用与您尝试的方法类似的方法,我有一个monad叫做TryOption<T>a Try和an Option。但是现在我可以用HKT的新东西写东西了Try<Option<T>>。原始实现在这里:

    无论如何,我希望能有所帮助!

    2023-02-13 11:04 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有