IOmonad在C#这样的语言中有意义吗经过大量的阅读和思考,我想我终于掌握了什么是monad,它们如何工作,以及它们有什么用。我的主要目标是弄清楚monad是否适用于我在C#中的日常工作。当我开始学习monad时,给我的印象是它们很神奇,它们以某种方式使IO和其他不纯的函数变得纯洁。我了解monad对于.Net中的LINQ之类的东西有多重要,并且Maybe非常适合处理不返回有效值的函数。我也很欣赏在代码中限制状态和隔离外部依赖的需要,我希望monads也能帮助他们。但我最终得出结论,用于IO和处理状态的monads在Haskell中是必须的,因为Haskell没有其他方法可以做到(否则,你不能保证顺序,一些调用会被优化掉。)但是对于更主流的语言,monad不适合这些需求,因为大多数语言已经很容易处理状态和IO。所以,我的问题是,说IOmonad真的只对Haskell有用吗?在C#中实现IOmonad有充分的理由吗?我经常使用Haskell和F#,但我从未真正觉得有必要在F#中使用IO或状态monad。我的主要原因是在Haskell中,你可以从不使用IO或状态的东西的类型中分辨出来,这是非常有价值的信息。在F#(和C#)中,对其他人的代码没有这样的一般期望,因此您不会从将自己的代码添加到自己的代码中获得太多好处,并且您将支付一些通用开销(主要是语法)来保留它.由于缺乏更高级别的类型,Monad在.NET平台上也不能很好地工作:虽然您可以使用工作流语法在F#中编写monadic代码,但在C#中它更痛苦并且您不能轻松编写抽象多个不同单子的代码。在工作中,我们使用monad来控制我们最重要的业务逻辑C#代码中的IO。两个例子是我们的财务代码和为我们的客户找到优化问题解决方案的代码。在我们的财务代码中,我们使用monad来控制IO写入和读取数据库。它基本上由一组操作和一个用于monad操作的抽象语法树组成。你可以这样想象它(不是实际的代码):RPostTransaction(PostTransaction操作);}interfaceIFinancialOperation{RAccept(IFinancialOperationVisitorvisitor);}classGetTransactions:operation>IFinancialO{AccountAccount{get;放;};publicRAccept(IFinancialOperationVisitorvisitor){returnvisitor.Accept(this);}}classPostTransaction:IFinancialOperation>{交易交易{get;放;};publicRAccept(IFinancialOperationVisitorvisitor){returnvisitor.Accept(this);这本质上是用于数据FinancialOperationa的Haskell代码,其中GetTransactions::Account->FinancialOperation(EitherError[Transaction])PostTransaction::Transaction->FinancialOperation(EitherErrorUnit)以及为monad中的操作构造抽象语法树,基本上是一个免费的monad:interfaceIMonadicActionVisitor{RReturn(Tvalue);RBind(IMonadicAction输入,Func>投影);R失败(错误错误);}//对象到rem嵌入参数,并将它们传递给访问者,就像上面一样/*希望我在没有高阶类型的情况下在所有事情上都得到了正确的方差,这就是我们过去这样做的方式。我们现在在C#中使用高阶类型,更多内容见下文。这里,为了避免高阶类型,monadic动作的AST被继承包含在*/中,在实际代码中,这样的更多,所以我们可以记住某些东西被继承包含在*/财务操作中,包括使用.Select()而不是.SelectMany()构建的中间计算仍然具有IFinancialOperation类型。操作的实际执行是由解释器完成的,它将所有数据库操作包装在一个事务中,并处理在任何组件不成功时回滚事务的方法。我们还使用解释器对代码进行单元测试。在我们优化的代码中,我们使用monads来控制外部数据的IO来进行优化。这允许我们编写不知道计算是如何组成的代码,这允许我们在多个设置中使用完全相同的业务代码:由于代码需要传递给monad以供使用,因此我们需要明确定义单子。这是一个。IEncapsulate本质上意味着TClass。这允许c#编译器同时跟踪monad类型的所有三个部分,从而克服了在处理monad时自己进行转换的需要。公共接口IEncapsulated{TClass类{得到;}}publicinterfaceIFunctorwhereF:IFunctor{//MapIEncapsulatedSelect(IEncapsulatedinitial,Funcprojection);}publicinterfaceIApplicativeFunctor:IFunctorwhereF:IApplicativeFunctor{//Return/PureReturned(Avalue);IEncapsulatedApply(IEncapsulated投影,IEncapsulated初始);}publicinterfaceIMonad:IApplicativeFunctorwhereM:IMonad{//BindIEncapsulatedSelectMany(IEncapsulatedinitial,Func>binding);//绑定和投影IEncapsulatedSelectMany(IEncapsulatedinitial,Func>binding,Funcprojection);}publicinterfaceIMonadFail:IMonad{//失败IEncapsulatedFail(TErrorerror);现在我们可以想象为我们的计算需要能够看到的IO部分制作另一种monad:publicinterfaceIMonadGetSomething:IMonadFail{然后我们可以编写不知道计算如何组合在一起的代码lt=monad.Return(Enumerable.Empty());//我们的开发人员可能仍然喜欢编写命令式代码for(inti=0;ix.ToList());这可以在IMonadGetSomething的同步和异步实现中IMonadGetSomething请注意,在这段代码中,GetSomething()将一个接一个地发生,直到发生错误,即使在异步设置中也是如此。(不,这不是我们在现实生活中构建列表的方式)你在问“我们需要C#中的IOmonad吗?”但你应该问“我们需要一种方法来可靠地获得C#的纯度而不是变性?”。主要好处是控制副作用。无论您是使用monad还是其他机制来执行此操作都没有关系。例如,C#可能允许您将方法标记为纯方法,将类标记为不可变。这对于抑制副作用非常有用。在这样一个假设的C#版本中,您将尝试使90%的计算纯化,并为剩余的10%提供不受限制的急切IO和副作用。在这样的世界中,我认为不需要绝对纯度和IOmonad。请注意,通过机械地将副作用代码转换为monadic样式,您将一无所获。代码根本不会提高质量。您可以提高90%纯度的代码质量,并将IO集中到小的、易于查看的地方。仅通过查看函数的签名来判断函数是否有副作用的能力在试图理解函数的作用时非常有用。功能越少,越看不懂!(多态性是另一种有助于限制函数可以对其参数执行的操作的东西。)在许多实现软件事务内存的语言中,文档都有这样的警告:AvoidI/Oandothertransactionsthathavesideeffectsactivitybecausethe交易将被重试。将该警告变成类型系统强制执行的禁令会使语言更安全。只能使用没有副作用的代码执行优化。但是,如果您首先“允许任何事情”,则很难确定没有副作用。IOmonad的另一个好处是,由于IO操作是“惰性的”,除非它们位于main函数的路径中,因此很容易将它们作为数据进行操作,将它们放入容器中,在运行时组合它们等。当然,IO的monadic方法有其缺点。但它除了是“以一种灵活而有原则的方式使用纯惰性语言进行I/O的少数方法之一”之外,确实具有其他优势。与往常一样,IOmonad很特殊且难以推理。在Haskell社区中众所周知,虽然有用,但IO并没有分享其他monad的许多好处。正如您所说,它的实用性极大地激发了它的特权地位,而不是它是一个很棒的建模工具。有了它,我会说它在C#中没有那么有用,或者实际上,任何不尝试完全包含带有类型注释的副作用的语言。但它只是一个单子。正如您提到的,失败来自于LINQ,但即使在副作用语言中,更复杂的monad也很有用。例如,即使在任意全局和局部状态的上下文中,状态monad也将指示作用于某些特权状态的行为机制的开始和结束。你不会得到Haskell喜欢的副作用消除保证,但你仍然可以获得很好的文档。更进一步,引入像Parsermonad这样的东西是我最喜欢的例子。即使在C#中,拥有该monad也是处理非确定性、回溯失败等的好方法。在使用字符串时将事情本地化。显然,您可以通过特定类型的可变性来做到这一点,但Monad代表在有效机制中执行有用操作的特定表达式,而不管您可能还涉及的任何全局状态。所以,我会说是的,它们很有用用任何一种语言。但是像Haskell那样的IO能做到吗?也许不是那么多。在像C#这样可以在任何地方执行IO的语言中,IOmonad并没有真正起到任何实际作用。唯一一次你想使用它来控制副作用,因为没有什么能阻止你在monad之外执行副作用,所以没有太多意义。至于Maybemonad,虽然它看起来很有用,但它只适用于惰性求值的语言。在下面的Haskell表达式中,如果第一次查找返回Nothing,则不计算第二次查找:doSomething::String->MaybeIntdoSomethingname=dox这允许表达式在遇到Nothing时“短路”。C#中的实现必须进行两次查找(我想,我对看到一个反例很感兴趣。)您最好使用if语句。另一个问题是抽象的丢失。虽然在C#中实现monad(或看起来有点像monad的东西)当然是可能的,但您不能像在Haskell中那样真正概括,因为C#没有更高的种类。例如,像mapM::Monadm=>Monadm=>(a->mb)->[a]->m[b](对于任何monad)这样的函数不能真正用C#表达。你当然可以这样做:publicList);它适用于特定的monad(在本例中为Maybe),但不可能从该函数中抽象出Maybe。您必须能够执行以下操作:publicList(Func>);这在C#中是不可能的。以上就是C#学习教程的全部内容:IOmonad在C#和其他语言中的意义。如果对大家有用,需要了解更多C#学习教程,希望大家多多关注---本文来自网络收集,不代表立场,如涉及侵权,请点击右边联系管理员删除。如需转载请注明出处:
