【问题标题】:Standard Operations on Option<T> monad beyond Bind and MapBind 和 Map 之外的 Option<T> monad 的标准操作
【发布时间】:2013-04-04 00:42:53
【问题描述】:

我正在使用 Petricek 书(真实世界函数式编程)中 F# option&lt;`a`&gt; monad 的 C# 实现:

internal enum OptionType { Some, None }

internal abstract class Option<T>
{
    private readonly OptionType tag;

    protected Option(OptionType tag)
    {
        this.tag = tag;
    }

    public OptionType Tag
    {
        get { return this.tag; }
    }

    public bool MatchNone()
    {
        return this.Tag == OptionType.None;
    }

    public bool MatchSome(out T value)
    {
        if (this.Tag == OptionType.Some)
        {
            value = ((Some<T>)this).Value;
        }
        else
        {
            value = default(T);
        }
        return this.Tag == OptionType.Some;
    }
}

internal sealed class None<T> : Option<T>
{
    public None() : base(OptionType.None) { }
}

internal sealed class Some<T> : Option<T>
{
    private readonly T value;

    public Some(T value)
        : base(OptionType.Some)
    {
        this.value = value;
    }

    public T Value
    {
        get
        {
            return this.value;
        }
    }
}

internal static class Option
{
    public static Option<T> None<T>()
    {
        return new None<T>();
    }

    public static Some<T> Some<T>(T value)
    {
        return new Some<T>(value);
    }
}

internal static class OptionExtensions
{
    public static Option<T2> Bind<T1, T2>(this Option<T1> option, Func<T1, Option<T2>> func)
    {
        T1 value1;
        if (option.MatchSome(out value1))
        {
            return func(value1);
        }
        return Option.None<T2>();
    }

    public static Option<T2> Map<T1, T2>(this Option<T1> option, Func<T1, T2> func)
    {
        T1 value1;
        if (option.MatchSome(out value1))
        {
            return Option.Some(func(value1));
        }
        return Option.None<T2>();
    }
}

现在我需要一个操作来提取值,如果它不是None 或返回一个默认值。

我想知道这是否可以使用 MapBind 的组合,但我认为不是。

所以我回到F# documentation,它给了我一些其他有用的扩展方法的提示,但不完全是我需要的。

我设计了这个函数来满足我的需要:

public static T2 Return<T1, T2>(this Option<T1> option, Func<T1, T2> func, T2 noneValue)
{
    T1 value1;
    if (option.MatchSome(out value1))
    {
        return func(value1);
    }
    return noneValue;
}

所以为了不重新发明轮子,问题是:是否有参考或通用功能模式来定义Option&lt;T&gt; monad 上的操作? 根据需要添加新操作是否正确?

【问题讨论】:

    标签: c# functional-programming monads


    【解决方案1】:

    一个你总是可以提取值的单子通常是一个comonad。你知道 monad M&lt;T&gt; 有方法(在 C# 语法中)

    static M<T> Unit<T>(T t) { ... }
    static M<R> Bind<A, R>(M<A> ma, Func<A, M<R>> func) { ... }
    

    或者,您也可以制作一个单子

    static M<T> Unit<T>(T t) { ... }
    static M<R> FMap<A, R>(M<A> ma, Func<A, R> func) { ... }
    static M<T> Join<T>(M<M<T>> mmt) { ... }
    

    这两种表征是等价的;您可以构建另一个的给定实现。

    comonad 有操作

    static T Extract<T>(M<T> mt) { ... } 
    static M<R> Extend<A, R>(M<A> ma, Func<M<A>, R> func) { ... }
    

    Extract 是 Unit 的“对立面”,Extend 是 Bind 的“对立面”。

    或者,您也可以使用以下操作定义一个comonad:

    static T Extract<T>(M<T> mt) { ... } 
    static M<R> FMap<A, R>(M<A> ma, Func<A, R> func) { ... }
    static M<M<T>> Duplicate<T>(M<T> mt) { ... }
    

    其中 Duplicate 是 Join 的“对立面”。同样,这两种表征是等价的;给定一个,您可以构建另一个。

    显然,您不能仅在给定 Bind、Unit、FMap 和 Join 的情况下实现 Extract,因为 这些都不会以任何方式返回 T,而它是您需要的 T。

    对于任一版本的comonad,您遇到的问题是可选的monad真的不是comonad,因为如果monadic值“缺失”,就没有自然的方法来实现Extract。

    现在,如果你愿意,你可以做Nullable&lt;T&gt; 所做的事情。 Nullable&lt;T&gt;.GetValueOrDefault() 如果有则返回值,default(T) 如果没有则返回。如果您想将 Optional 设置为共单,这可能是您可以在这里做的最好的事情。

    【讨论】:

    • +1 这很清楚地回答了我的问题。还赞赏术语(comonad)的澄清。
    • @jay:不客气。我最近在我的博客上写了 13 篇系列文章,讨论 C# 程序员的 monad。看看这个主题是否让你感兴趣。 ericlippert.com/category/monads
    • 感谢埃里克。我已经是你博客上这个系列和其他系列的读者了。最后一个关于monads,我不得不说这真的很有趣。 :)
    猜你喜欢
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 2017-12-06
    • 1970-01-01
    相关资源
    最近更新 更多