Main Nightly

Either

Either 是你在 Swift 中已经知道的类型之一,但它专门用于某些值。

这就是它

enum Either<Left, Right> {
    case left(Left)
    case right(Right)
}

Slo 它是一个具有两种情况的枚举。这至少应该让你想起两件事。通常 right 的情况与 good(好)或 success(成功)或 dextra(拉丁语“右”)相关联。另一方面,left 的情况用于 bad(坏)、failure(失败)、error(错误),或者你可以说是 sinistra(拉丁语“左”)。

至少 Swift 中常见的两种类型是 Either 的特化。

你可以看看 Optional,并将 leftnone 情况匹配。你也可以看看 Result,并将 failureleft 情况匹配。

所以这里你有一个更通用的类型可以使用。它不仅在你需要验证内容时有用。而且在需要返回不同类型时也很有用。

计算属性

为了让生活更轻松一点,这里定义了一些辅助属性。

用于逻辑检查的便捷属性。

var isLeft : Bool // true if `left` case
var isRight: Bool // true if `right` case

当你想获取一个值,但可以处理 optional 时。

var right: Right?
var left : Left?

看看 OptionalAPI,了解如何愉快地使用 Optional。

maps

func map<R>(_ transform: (Right) -> R) -> Either<Left,R>

这是一个函数,它接受一个函数,该函数期望 Right 的一个实例,并生成一个新的 Either<Left, R> 实例。类型 R 意味着 NewRight,但被缩写了。

Either 对其 right 值进行了一些特殊处理。这类似于 Result 和 Optional 如何处理它们自身的一些情况。

这意味着如果你想 map 一个 Either,那么默认情况下你将被赋予一个 Right 类型的实例。

所以如果你有

func increment(_ i: Int) -> Int { i + 1 }

let right = Either<String,Int>.right(42)

right
    .map(increment) // .right(43)

最终结果是一个新的 Either<String,Int> 实例,它的 right 情况持有 43。对 left 做同样的事情将不会有任何效果。

let left = Either<String,Int>.left("I'm left")

left
    .map(increment) //  .left("I'm left")

rightMap

func mapRight<R>(_ transform: (Right) -> R) -> Either<Left, R>

你可以显式地调用 rightMap,它只是 map 的一个包装器。

leftMap

func mapLeft<L>(_ transform: (Left) -> L) -> Either<L, Right>

但是,如果你想转换一个 left 值,你可以使用 leftMap。与 right 相同的故事,但针对 left。

left
    .map({ $0.uppercased() }) // .left("I'M LEFT")

biMap

func biMap<L,R>( _  leftTransform:  (Left) -> L,
                 _ rightTransform: (Right) -> R)
     -> Either<L, R>

这个函数接受另外两个函数作为参数。“Left” 是left 转换,“right” 是 right 转换。这会产生一个新的 Either<L,R> 实例。

这一个 map 结合了 leftMaprightMap。更常见的名称是 biMap,但我希望它在 IDE 中方便使用,你可以开始输入 map 并查看那里有什么。

当你想将这些转换组合成一个语句时,可以使用这个 biMap

right
    .biMap({ $0.uppercased() }, increment) // .right(43)

left
    .biMap({ $0.uppercased() }, increment) // .left("I'M LEFT")

flatMaps

与上面的 map 相同,但有一个区别。

func flatMap<R>(
    _ transform: (Right) -> Either<Left, R> 
    ) -> Either<Left, R>

func flatMapLeft<L>(
    _ transform: (Left) -> Either<L, Right>
    ) -> Either<L, Right>

这次转换函数返回另一个 Either。这将导致一个 Either Either,例如 Either<Left, Either<Left, R>>。因此,使用这个 flatMap(或者你可以找到的另一个名称 bind),你可以移除一层嵌套。

作为一个旁注,每当你使用 flatMap 时,你都在进行可怕的 monadic 计算。你曾经在 optionals 上调用 flatMap,并且一切都很好。是的,Optional 是一个 Monad。如果你想了解更多,你可以 Google 它,只是不要想太多,你会没事的 😎

工具

either

func either<L,R,T>(
    _  leftTransform: @escaping (L) -> T,
    _ rightTransform: @escaping (R) -> T) -> (Either<L,R>) -> T

这可能不是很常见,但它是一个返回另一个函数的函数。它的工作方式是你首先提供两个函数。“Left 转换” 知道如何给定一个 L 来产生 T。“Right 转换” 知道如何给定一个 R 来产生 T

下一步是返回的函数。这个函数期望一个 Either<L,R>,当你提供这个 either 的一个实例时,它将产生一个 T 的实例。

lefts

func lefts<L,R>(_ eithers: [Either<L,R>] ) -> [L]

我猜类型说明了一切。给这个函数一个 either 数组,作为回报,你将得到一个 L 的数组。

rights

func rights<L,R>(_ eithers: [Either<L,R>] ) -> [R]

我猜类型说明了一切。给这个函数一个 either 数组,作为回报,你将得到一个 R 的数组。

partitionEithers

返回一个元组,其中包含 left/第一个数组中的所有 L 值。以及 right/第二个数组中的所有 R 值。

let eithers: [Either<String,Int>] = 
    [ .left("A"), 
      .right(42), 
      .left("B"), 
      .right(24) ]

let (lefts, rights) = partitionEithers(eithers)

lefts  // ["A", "B"]
rights // [42, 24]

YouTube

如果你懂 🇵🇱 波兰语,那么你可以查看这个 YT 播放列表 Either - Monada Either w Swift

🐇🕳 Rabbit Hole

这个项目是 🐇🕳 Rabbit Hole Packages Collection 的一部分