如何制作StopExecutingWhenYouHaveAResultAndReturnThatResult monad

时间:2011-11-16 07:32:58

标签: c# linq monads

我特别需要一种反向的可能monad,当它是“Nothing”时会继续尝试获取一个值,并保持它获得的第一个有效值。

我试图概述我的意思:

public void Test()
{
    var r = from x in SomeActionToGetResult()
            from y in Stop()
            from z in SomeOtherActionToGetResult()
            select x | y | z;
}

public Maybe<Result> SomeActionToGetResult()
{
    return new Result().Unit();
}

public Maybe<Result> SomeOtherActionToGetResult()
{
    return new Result().Unit();
}

public Maybe<Result> Stop()
{
    return new Nothing<Result>();
}

Result类如下所示:

public class Result
{
    public static Result operator |(Result a, Result b)
    {
        if (a != null)
            return a;

        return b;
    }
}

单位功能与正常情况一样,但是绑定功能给我带来了麻烦:

public static Maybe<U> Bind<T, U>(this Maybe<T> m, Func<T, Maybe<U>> k)
{
    if (!m.HasAValidValue)
        return k(m.Value);

    return m; // <--- problem right here
}

可以看出,我会 - 鉴于m没有价值 - 尝试运行k()以获得有效值。如果m已有值,我将使用该值,并且永远不会运行k()

T和U从同一类型开始 - 但只要linq查询中的匿名类型启动,我就不能再依赖它了。

这有可能吗?

1 个答案:

答案 0 :(得分:1)

(这个可能对你没有任何帮助,在这种情况下我会删除它。如果你正在玩monad这很可能教我的祖母吸蛋,但是...)

您是否知道C#已经在语言级别使用null-coalescing operator

var r = SomeActionToGetResult() ?? Stop() ?? SomeOtherActionToGetResult();

如果你需要在一个更基于库的形式中实现这一点,那很好(我会看到我能想到的)但是如果你可以只使用现有的运算符,我会

编辑:我已经解决了你的难题,我认为基本上它不适合LINQ。问题主要与LINQ在SelectMany中期望的类型有关。例如,您的第二行有效地转换为:

SomeActionToGetResult().SelectMany(x => Stop(), (x, y) => new { x, y });

问题在于:

  • 我们x表达式中使用Stop()的值,逻辑不能,因为我们只希望它如果x评估为None
  • ,则执行此操作
  • 匿名类型必须与返回类型T的{​​{1}}部分类型不同,因此我们无法将其转换为SomeActionToGetResult()。我们必须引入一种新类型来提取它们。

然后我们有:

Maybe<T>

这在逻辑上是唯一的东西吗?

是否有意义?
select x | y | z

如果是这样,那意味着什么?我们不能避免select z | y | x 被执行开始,因为扩展方法只适用于该方法调用的结果...所以要么必须忽略投影而你始终评估SomeActionToGetResult(),然后评估x,然后评估y - 或者您接受排序并不总是完全保留。

从根本上说,我怀疑你