DotNETWeekly-io / DotNetWeekly

DotNet weekly newsletter
MIT License
207 stars 3 forks source link

【文章推荐】Option Monad #740

Open gaufung opened 2 days ago

gaufung commented 2 days ago

https://www.youtube.com/watch?v=Phn2Mu8zoxM&ab_channel=MilanJovanovi%C4%87

gaufung commented 18 hours ago

image

Option Monad 是一种编程范式,它可以解决编程中 Null 问题,在 C# 中我们可以使用 nullable 类型来解决这个问题,但是 Option Monad 已经广泛使用在其他编程语言中。

首先我们定义个 Mayb<T> 的类型

public abstract class Maybe<T>
{
    private Maybe()
    {

    }

    public static Maybe<T> None => new NULL();

    private sealed class NULL : Maybe<T>
    {

    }

    public sealed class Some : Maybe<T>
    {
        public Some(T value) => Value = value;
        public T Value { get; }
    }

    public bool TryGetValue(out T value)
    {
        if (this is Some some)
        {
            value = some.Value;
            return true;
        }
        else
        {
            value = default(T);
            return false;
        }
    }

    public TResult Match<TResult>(Func<T, TResult> some, Func<TResult> none)
    {
        if (this is Some s)
        {
            return some(s.Value);
        }
        else
        {
            return none();
        }
    }

    public void Match(Action<T> some, Action none)
    {
        if (this is Some s)
        {
            some(s.Value);
        }
        else
        {
            none();
        }
    }
}

这里 Maybe<T> 是一个抽象类型,而 Some<T>Null<T> 是两个具体的子类,而且定义 None 的静态属性为 Null<T>的具体实现。除此之外,还定义了 TryGetValueMatch 两个方法方便使用。

这样我们的方法定义的返回值如下

Maybe<string> GetLogContent(int id)
{
    var filename = $"log{id}.txt";
    if (File.Exists(filename))
    {
        return new Maybe<string>.Some(File.ReadAllText(filename));
    }
    else
    {
        return Maybe<string>.None;
    }
}

那么使用的使用有很多方法

var content = GetLogContent(1);

// Type Cast
if (content is Maybe<string>.Some some)
{
    Console.WriteLine(some.Value);
}
else
{
    Console.WriteLine("No content found");
}

// TryGetValue
if (content.TryGetValue(out var value))
{
    Console.WriteLine(value);
}
else
{
    Console.WriteLine("No content found");
}

// Match
content.Match(
    some: value => Console.WriteLine(value),
    none: () => Console.WriteLine("No content found")
);