String.IsNullOrEmpty Monad

问题描述 投票:0回答:3

我最近开始涉足函数式编程的迷人世界,这主要是因为在 React 等 FP 平台上获得了经验,并阅读了诸如 https://blog.ploeh.dk/ 之类的博客。作为一名主要命令式程序员,这是一个有趣的转变,但我仍在努力适应。

我对使用

string.IsNullOrEmpty
有点厌倦了。很多时候我发现自己在代码中乱扔了诸如

之类的表达式
_ = string.IsNullOrEmpty(str) ? "default text here" : str;

这并没有那么糟糕,但是假设我想将一堆选项链接到那个空值之后,例如

_ = string.IsNullOrEmpty(str) ? (
    util.TryGrabbingMeAnother() ??
    "default text here") : str;

恶心。我更想要这样的东西--

_ = monad.NonEmptyOrNull(str) ??
    util.TryGrabbingMeAnother() ??
    "default text here";

如示例所示,我正在使用一个称为 monad 的函数来帮助将

string.IsNullOrEmpty
简化为可空链接操作:

public string NonEmptyOrNull(string source) =>
    string.IsNullOrEmpty(source) ? null : source;

我的问题是,这是正确的术语吗?我知道

Nullable<T>
可以被认为是一个 monad(参见 Nullable 可以在 C# 中用作函子吗?简单英语中的 Monad?(对于没有 FP 背景的 OOP 程序员))。这些材料是很好的参考,但我对这个主题仍然没有足够的直观把握,不知道我在这里是否只是感到困惑或不一致。例如,我知道 monad 应该像我上面那样启用函数链接,但它们也是“类型放大器”——所以我的小例子似乎表现就像一个用于启用链接的 monad,但似乎将 null/empty 转换为 null 是一个reduction而不是放大,所以我怀疑这是否实际上is是一个 monad。那么对于这个特定的应用程序,对 FP 有更多经验的人可以告诉我将
NonEmptyOrNull
称为 monad 是否准确,为什么或为什么不准确?

c# string functional-programming null monads
3个回答
2
投票

单子是由以下部分组成的三元组:

  • 单参数类型构造函数
    M
  • 类型为
    unit
     的函数 
    a -> M a
  • 类型为
    join
     的函数 
    M (M a) -> M a

满足单子定律。

类型构造函数是一个类型级函数,它接受多个类型参数并返回一个类型。 C# 不直接具有此功能,但在编码 monad 时,您需要一个单参数泛型类型,例如

List<T>
Task<T>
等。对于某些泛型类型
M
,您因此需要两个函数来从单个值构造泛型类型的实例,一个“展平”该类型的嵌套实例。例如对于
List<T>
:

public static List<T> unit<T>(T value) { return new List<T> { value }; }
public static List<T> join<T>(List<List<T>> l) { return l.SelectMany(l => l); }

从这个定义中你可以看到单个函数不能满足 monad 的定义,所以你的例子不是 monad 的例子。

根据这个定义,

Nullable<T>
也没有monad实例,因为嵌套类型
Nullable<Nullable<T>>
无法构造,因此
join
无法实现。


1
投票

这更像是一个 filter 操作。在 C# 中,您习惯性地将其称为

Where
。如果我们更明确地区分缺失值和填充值,可能会更容易看出,我们可以使用 Maybe 容器:

public static Maybe<T> Where<T>(
    this Maybe<T> source,
    Func<T, bool> predicate)
{
    return source.SelectMany(x => predicate(x) ? x.ToMaybe() : Maybe.Empty<T>());
}

只有少数容器支持过滤。最常见的两个是

Maybe
(又名
Option
)和各种集合(即
IEnumerable<T>
)。

在 Haskell 中(它具有比 C# 更强大的类型系统)这是通过名为 MonadPlus

 的类启用的
,但我认为类型类
Alternative
实际上应该足以实现过滤。
Alternative
被描述为作为应用函子上的幺半群。但我不确定这是否特别有帮助。

使用上述

Where
方法,您可以通过像
Maybe
这样的检查来线程化
IsNullOrEmpty
值:

var m = "foo".ToMaybe();
var inspected = m.Where(s => !string.IsNullOrEmpty(s));

这将使

m
不变地通过,而以下不会:

var m = "".ToMaybe();
var inspected = m.Where(s => !string.IsNullOrEmpty(s));

你可以对

Nullable<T>
做同样的事情,但我会把它作为练习😉

您也可以使用 C# 8 的新的 可空引用类型 语言功能来实现此目的,但我还没有尝试过。


0
投票

我相信这通常在 FP 范式中比验证提前一步得到解决

null
str
值绝不能是
null
。相反,原始方法必须返回一个空集合。这样,方法的链接就不必验证 null。由于没有可操作的元素,因此不会执行下一个操作

您可以找到多个参考资料。网上有与此相关的内容。 https://www.informit.com/articles/article.aspx?p=2133373&seqNum=5是我可以快速抓住的

我从 Pluralsight 的 Zoran Horvat 课程中学到了这一点。如果您有访问权限,请检查一下。课程名称是“ .NET 中的战术设计模式:控制流”,模块是“空对象和特殊情况模式”

谈到对 FP 的兴趣,Zoran Horvat 还有其他课程可以帮助转换或使 OO 代码更实用。我很高兴在这里做出回应,因为最近我也一直在研究 FP。祝你好运!

© www.soinside.com 2019 - 2024. All rights reserved.