如何制作 StopExecutingWhenYouHaveAResultAndReturnThatResult monad

发布于 2024-12-16 00:57:52 字数 1242 浏览 1 评论 0原文

我特别需要一种反向 Maybe 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;
    }
}

Unit 函数就像正常的一样,但是 Bind 函数给我带来了麻烦:

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 查询中的匿名类型开始使用,我就不能再依赖它了。

这有可能做到吗?

I have a special need for a kind of reverse Maybe monad that when being "Nothing" will continue to try to get a value and will keep the first valid value that it gets.

I have tried to outline what I mean here:

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>();
}

The Result class looks like this:

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

        return b;
    }
}

The Unit function is like normal, but the Bind functions gives me trouble:

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
}

As can be seen, I will - given that m has no value - try to run k() to obtain a valid value. If m already has a value I will like to use that and never run k().

T and U start out as the same type - but as soon as the anonymous types in the linq query kicks in, I can no longer rely on that.

Is this at all possible to do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

叫思念不要吵 2024-12-23 00:57:52

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

您是否知道 C# 已经在语言级别使用 null-coalescing 运算符实现了这一点

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

如果您需要以更加基于库的形式实现这一点,那很好(我会看看我能想到什么),但如果您可以只使用现有的运算符,我会的。

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

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

这里的问题是:

  • 我们Stop() 中使用 x 的值> 表达式,逻辑上不能,因为我们只希望它在 x 计算结果为 None 时执行
  • 匿名类型不一定相同type 作为返回类型的 T 部分SomeActionToGetResult(),因此我们无法将其转换为 Maybe。我们必须引入一种新类型来提取它们。

然后当我们有:

select x | y | z

从逻辑上讲,这是你可以拥有的唯一东西吗?拥有它有意义吗

select z | y | x

?如果是的话,那意味着什么?我们无法避免 SomeActionToGetResult() 从一开始就被执行,因为扩展方法仅适用于该方法调用的结果......所以要么投影有被忽略,并且您总是评估 x,然后评估 y,然后评估 z - 或者您接受顺序不会总是被准确保留。

从根本上说,我怀疑你这里没有真正一个单子,否则我希望它适合 - 但我在数学方面还没有足够的能力来说。

(This may not be any help to you, in which case I'll just delete it. If you're playing around with monads this is quite possibly teaching my grandmother to suck eggs, but...)

Are you aware that C# already has this at a language level with the null-coalescing operator?

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

If you need to achieve this in a more library-based form, that's fine (and I'll see what I can think of) but if you can just use the existing operator, I would.

EDIT: I've had a go at your puzzle, and I think basically it's not a good fit for LINQ. The problems are mostly to do with the types that LINQ expects in SelectMany. For example, your second line is effectively transformed to:

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

Here the problems are:

  • We don't use the value of x in the Stop() expression, and logically can't as we only want it to be executed if x evaluates to None
  • The anonymous type is necessarily not the same type as the T part of the return type of SomeActionToGetResult(), so we can't convert it into a Maybe<T>. We'd have to introduce a new type to extract them both.

Then later when we have:

select x | y | z

Is that logically the only thing you could have? Would it make sense to have

select z | y | x

and if so, what would that mean? We can't avoid SomeActionToGetResult() from being executed to start with, as the extension method only works on the result of that method call... so either the projection has to be ignored and you always evaluate x, then y, then z - or you accept that the ordering won't always be preserved exactly.

Fundamentally I suspect you haven't really got a monad here, otherwise I'd expect it to fit - but I'm not really competent enough in the maths to say.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文