处理 Option 和 Either 类型 - 惯用转换?

Posted

技术标签:

【中文标题】处理 Option 和 Either 类型 - 惯用转换?【英文标题】:Dealing with Option and Either types - idiomatic conversions? 【发布时间】:2011-11-22 07:54:10 【问题描述】:

我可能在文档中遗漏了一些正确的东西,但我无法真正理解它 - 我一直在通过反复试验来自学 Scala。

给定一个函数f: A => C,执行以下转换的惯用方法是什么?

Either[A, B] -> Either[C, B]

Either[B, A] -> Either[B, C]

(如果我有两个这样的函数,并且想转换两边,我可以一次完成还是应该顺序应用两次成语?)

Option[A] -> Option[C]

(我感觉这应该以某种方式使用for (...) yield;我可能只是对此一无所知,当我看到答案时会觉得很傻)

究竟Either的“投影”吗?

【问题讨论】:

从下面的评论中,您似乎将EitherPairTuple2 混淆了。情况并非如此:Either 只能包含 one 值。从某种意义上说,它类似于 C 中的union,只是你知道实际存储的是哪个成员。 我知道这一点,但我仍然对一个预测被混入其中感到困惑。我并没有真正表达清楚。 【参考方案1】:

您可以选择:

either.left.map(f)

或一个:

either.right.map(f)

您也可以使用理解:for (x <- either.left) yield f(x)

这是一个更具体的例子,在 Either[Boolean, Int] 上执行 map

scala> val either: Either[Boolean, Int] = Right(5)
either: Either[Boolean, Int] = Right(5)

scala> val e2 = either.right.map(_ > 0)
either: Either[Boolean, Boolean] = Right(true)

scala> e2.left.map(!_)
either: Either[Boolean, Boolean] = Right(true)

编辑:

它是如何工作的?假设您有一个Either[A, B]。调用leftright 会创建一个LeftProjectionRightProjection 对象,该对象是一个包含Either[A, B] 对象的包装器。

对于left 包装器,应用带有函数f: A => C 的后续mapEither[A, B] 转换为Either[C, B]。它通过在后台使用模式匹配来检查Either 是否实际上是Left。如果是,它会创建一个新的Left[C, B]。如果没有,它只会更改创建一个具有相同基础值的新Right[C, B]

对于 right 包装器,反之亦然。实际上,说 either.right.map(f) 意味着 - 如果任一 (Either[A, B]) 对象包含 Right 值,则映射它。否则,保持原样,但更改任一对象的类型 B,就像您已映射它一样。

所以从技术上讲,这些预测只是包装。从语义上讲,它们是一种表示您正在做某事的方式,假设存储在Either 对象中的值是LeftRight。如果这个假设是错误的,映射什么也不做,但是类型参数会相应地改变。

【讨论】:

请分解一下这对我有什么作用?跟预测有关系吗?显然,.left.right 与其他具有库“pair”类型的语言不同,它们只会分别访问 [A] 和 [B] 类型的成员。 我已经编辑了答案,以解释一些幕后发生的事情。但是,要么不是对,要么不是元组类型,也称为交集类型 - A x B。它拥有 [A] 或 [B] 类型的成员 - A V B。这意味着您不能像在元组中那样安全地访问任一成员,因为只有一个成员存在。 ...漂亮。那么选项呢?只需for (x <- option) yield f(x),因为 Option 已经是可迭代的? @Karl Option 不是Iterable,但它实现了方法map,这样就够了。 这里的问题是您需要提供 2 个映射函数来执行 Either 的实际映射,因为里面有 2 种可能的 types 值,并且你不知道它是什么。你可以这样做:e.left.map(!_).right.map(_ * 2)。你能从那里去寻找循环风格吗?可能是。让我考虑一下..【参考方案2】:

给定f: A=>BxOpt: Option[A]xOpt map f 会生成您需要的Option[B]

给定f: A=>BxOrY: Either[A, C]xOrY.left.map(f) 生成您正在寻找的Either,仅映射第一个组件;同样,您可以处理RightProjectionEither

如果您有两个函数,则可以为两个组件定义映射,xOrY.fold(f, g)

【讨论】:

【参考方案3】:
val e1:Either[String, Long] = Right(1)
val e2:Either[Int,Boolean] = e1.left.map(_.size).right.map( _ >1 )
// e2: Either[Int,Boolean] = Right(false)

【讨论】:

以上是关于处理 Option 和 Either 类型 - 惯用转换?的主要内容,如果未能解决你的问题,请参考以下文章

在 Flutter 中解压 Option<Either<T,T>>

scala中Either的一种使用场景

在scala中正确使用Either,Try和Exceptions / ControlThrowable

Kotlin Monad的学习

从 Either 返回定义的类型

在 Haskell 中组合任何类型的绑定