C#Continuation Monad实施

时间:2014-10-13 03:24:26

标签: c# haskell functional-programming monads

我一直致力于允许功能链接。我创建了一个名为continuationmonad的类,它接受一个值,一个函数来自a>湾这允许我使用fmap并绑定将这些链接在一起。我也使用懒惰来允许在可能的情况下进行呼叫。

这个类真的是延续monad还是别的东西。我发现很难找到不是Haskell的好文献。

还有关于如何改进/纠正此问题的任何意见。

using NUnit.Framework;
using System;

namespace Monads
{

    public class Continuation<Input, Output>{
        public Continuation(Input value, Func<Input,Output> function){
            this.value = new Lazy<Input>( () => value);
            this.function = function;
        }

        public Continuation(Lazy<Input> value, Func<Input,Output> function){
            this.value = value;
            this.function = function;
        }

        public Continuation<Output, Result> FMap<Result>(Func<Output, Result> map){
            return new Continuation<Output, Result>(new Lazy<Output>( () => Run() ), x => map(x));
        }

        public Continuation<Output,Result> Bind<Result>(Func<Output, Continuation<Output, Result>> f){
            return f(Run());
        }

        public Output Run(){
            return function(value.Value);
        }

        private Func<Input, Output> function;
        private Lazy<Input> value;
    }

    public static class ContinuationExtension{
        public static Continuation<A,B> Unit<A,B>(this Func<A,B> f, A value){
            return new Continuation<A, B>(value,f);
        }

        public static Continuation<A,B> Unit<A,B>(this A value,Func<A,B> f){
            return new Continuation<A, B>(value,f);
        }
    }

    [TestFixture]
    public class MonadTests
    {

        public Continuation<int,int> Wrapped(int value){
            return new Continuation<int,int>(value, x => x * 10);
        }

        [Test]
        public void ContinuationMonadTests()
        {

            var number = 42;
            var result = number.Unit(x => x + 8).FMap(x => x * 2).Bind(Wrapped).Run();

            Console.WriteLine(result);
        }
    }
}

3 个答案:

答案 0 :(得分:2)

这不是continuation monad。您更接近函数的Haskell Monad实例。

你没有得到任何使用Lazy<>无法获得的东西。由于您在构建类的实例时提供了输入,因此您不构建函数,而是构建由尚未计算的计算确定的值。 Lazy<>会延迟计算的评估,直到需要该值。

让我们为c#中的函数放置类似Haskell Monad实例的东西。 LINQ语法在c#中建立了Monad s的约定。他们应该:

  • 类似于Haskell的Select扩展方法Functor的{​​{1}}
  • 类似于Haskell fmap的{​​{1}}
  • SelectMany扩展方法
  • LINQ语法使用的附加Monad。这需要一个额外的功能,将两个步骤的值组合在一起。

不幸的是,对>>= SelectMany的模拟应该被称为什么没有约定;我们称之为Monad。不幸的是,return不是很方便,因为c#的类型推断将无法弄清楚类型。

Constant

请注意,定义这些扩展方法只允许您与类似Constant的内容进行交互,但它不允许您编写与正在使用的特定public static class Function { public static Func<TIn, TOut> Constant<TIn, TOut>(TOut result) { return x => result; } public static Func<TIn, TOut> Select<TIn, TMid, TOut>( this Func<TIn, TMid> func, Func<TMid, TOut> proj) { return x => proj(func(x)); } public static Func<TIn, TOut> SelectMany<TIn, TMid, TOut>( this Func<TIn, TMid> func, Func<TMid, Func<TIn, TOut>> proj) { return x => proj(func(x))(x); } public static Func<TIn, TOut> SelectMany<TIn, TMid1, TMid2, TOut>( this Func<TIn, TMid1> func, Func<TMid1, Func<TIn, TMid2>> proj1, Func<TMid1, TMid2, TOut> proj2) { return x => { var mid1 = func(x); var mid2 = proj1(mid1)(x); return proj2(mid1, mid2); }; } } 相关的通用代码。在second half of this answer中有一个如何做到这一点的草图。

答案 1 :(得分:2)

这可能有点基于意见,但无论如何我都会尝试给你5ct。

让我们来看看你的班级和他们的实例:

它包含一个值和一个函数,你试图让它变得懒惰。 从理论上看,我可以看到Lazy<T>乍一看没有区别:

您肯定可以将Continuation<Input,Output>之一转换为Lazy<Output>

反过来也是如此:给定一些惰性值a,你可以用

创建一个实例
new Continuation(a, x => x)

所以对我而言,似乎你刚刚重新发明了Lazy(这是一个monad,在Haskell你会称之为Identity

Cont monad并不容易崩溃,但它与.net-Events或.net-Observables更相关。数据结构本身就像

Func<Func<Input,Output>, Output>

在您继续Func<Input,Output>到某些内部计算的位置,然后在计算输入 {{1}时调用它获得最终结果。

这可能有点神秘,但是一个.net应用程序是F#使用的Input工作流,并且在某种意义上它代表了C#的异步/等待行为的模型。

我有一些材料用于C#on github对这个monad的简化版本的讨论,也许你会发现它很有趣。

答案 2 :(得分:0)

我已经对Continuation monad进行了非常全面的介绍,您可以在这里Discovering the Continuation Monad in C#

您还可以找到.Net小提琴here

我在这里总结总结一下 从初始功能开始

int Square(int x ){return (x * x);}
  1. 使用回调并删除返回类型
    public static void Square(int x, Action<int> callback)
    {
    callback(x * x);
    }
  1. 促成回调
    public static Action<Action<int>> Square(int x)
    {
     return (callback) => { callback(x * x); };
    }
  1. 概括返回的Continuation
    public static Func<Func<int,T>,T> Square<T>(int x)
    {
         return (callback) => { callback(x * x); };
    }
  1. 提取连续结构,也称为monad的返回方法。那就是给我一个价值,我会为此价值给你一个Monad
    //((U→ T) → T)

       delegate T Cont<U, T>(Func<U, T> f);

    public static Cont<U, T> ToContinuation<U, T>(this U x)
    {
       return (callback) => callback(x);
    }

    square.ToContinuation<Func<int, int>, int>()
  1. 添加bind Monadic方法,从而完成Monad。即给我两个Monad,然后将它们合并为一个新的monad

(((A→T)→T)→(A→((B→T)→T))→((B→T)→T)

    public static Cont<V, Answer> Bind<T, U, V, Answer>(
    this Cont<T, Answer> m,
    Func<T, Cont<U, Answer>> k, 
    Func<T, U, V> selector)
    {
     return (Func<V, Answer> c) => 
    m(t => k(t)(y => c(selector(t, y))));
    }