【发布时间】:2022-02-21 19:53:00
【问题描述】:
背景
我正在努力提高我的函数式编程 (FP) 技能,新手在 FP 中首先学习的东西之一是 Option 类型(又名 Maybe Monad)。
选择什么?
这种结构存在于许多语言中,Haskell 有 Maybe,Java 和 Python(是的,Python!)有 Optional。
基本上这种类型模拟一个可能存在也可能不存在的值。
这一切都归结为 Elixir
大多数 FP 语言都有 comprehensions,Scala 和 Elixir 有 for 构造,而 Haskell 有其著名的 do 表示法。
在 Scala 和 Haskell 中,这些推导不仅适用于 Enumerables(例如 Lists),而且适用于我们的 Option 类型(它不是可枚举的)。
我提到这一点,因为根据我的理解,Elixir 的理解仅适用于 Enumerables。此外,据我所知,Elixir 中没有 Option 类型的数据结构。
Elixir 有什么?
Elixir 以{:ok, val} 或{:error, reason} 的形式标记了元组。现在虽然 Elixir 推导式可以与标记的元组进行模式匹配:
iex> values = [good: 1, good: 2, bad: 3, good: 4]
iex> for {:good, n} <- values, do: n * n
[1, 4, 16]
它也会忽略不匹配的值:
iex> values = [good: 1, good: 2, bad: 3, good: 4]
iex> for {:bananas, n} <- values, do: n * n
[]
但是,这并不能正确复制 Option 类型的行为。以下是 Scala 中的示例:
for {
validName <- validateName(name)
validEnd <- validateEnd(end)
validStart <- validateStart(start, end)
} yield Event(validName, validStart, validEnd)
记住这个签名:
def validateName(name: String): Option[String]
def validateEnd(end: Int): Option[Int]
def validateStart(start: Int, end: Int): Option[Int]
如果任何函数返回 None ,完整理解表达式的结果将是 None。
使用 Elixir,糟糕的结果将被忽略,管道将从此愉快地继续下去。
问题
在这一点上,我认为将这个Option 类型实现为一个实现Enumerable 协议的结构(因此它可以在Elixir 理解中使用)应该是可能的。
但是,如果我可以使用元组模拟类似的行为,我不确定我是否愿意走这条路。
所以我有以下问题:
- 是否可以使用 Elixir 理解中的标记元组来模拟
Option类型? - 在Elixir 理解中是否有任何具有Monadic 类型(如我们在这里看到的)的Elixir 库? (我知道witchcraft,但他们有自己的理解结构,目前我认为这有点矫枉过正。我对与 Elixir 的原生理解功能一起工作的东西很感兴趣)。
【问题讨论】:
-
在我的经验中,选项类型是专门为静态类型语言实现的,它们将副作用转换为数据,我从未见过长生不老药中选项类型的必要性。 Python 很可能采用了这一点,以便他们可以更轻松地将人们迁移到他们的语言。
-
您可以使用 Elixir 以及通过 Dialyzer(我会推荐)进行相当程度的静态输入。尽管如此,我的问题仍未得到解答。
-
我不反对静态类型分析器,它们在某些地方很好。我讨厌看到人们在生产中实现这样的逻辑而不是使用官方方式,这样的项目不能很好地老化。 Javascript 和 Python 就是很好的例子,它们功能臃肿,却忽略了这些语言的设计目的。
-
我尊重你的意见。让我们暂时放弃这种友好的想法交流。
-
我不熟悉这个概念,也不知道 Elixir 的
for构造会跳过任何在<-左侧没有匹配的值 --我想这就像with语句,如果没有匹配,流被重定向(可选到else块)。这与定义 2 个函数子句(一个用于有效输入,一个用于处理“无”路径)有根本的不同吗?例如。def option(:foo), do: "Yes"和def option(_), do: nil
标签: elixir list-comprehension monads