正确使用不相交联合的右侧

using the right side of the disjoint union properly

将 Right[List] 转换为 List 的最佳方法是什么

我将像这样解析一个 Json 字符串

val parsed_states = io.circe.parser.decode[List[List[String]]](source)

这将创建一个等同于此的值

val example_data = Right(List(List("NAME", "state"), List("Alabama", "01"), List("Alaska", "02"), List("Arizona", "04")))

我正在尝试理解 RightLeftEither 并实施从上面的列表中获取 StateName、StateValue 对列表的最佳方法。

我发现这些方法中的任何一种都能满足我的需求(同时删除 header):

val parsed_states = example_data.toSeq(0).tail
val parsed_states = example_data.getOrElse(<ProbUseNoneHere>).iterator.to(Seq).tail
val parsed_states = example_data.getOrElse(<ProbUseNoneHere>).asInstanceOf[Seq[List[String]]].tail

我想我想知道我是否应该根据 io.circe.parser.decode 上游可能出现的行为以这种或另一种方式来做,或者我是不是想多了。我是 RightLeftEither 范式的新手,没有找到很多有用的例子。

回复@slouc

尝试将您的答案中的各个点联系起来,因为它们适用于此用例。所以像这样?

    def blackBox: String => Either[Exception, List[List[String]]] = (url:String) => {
      if (url == "passalong") {
        Right(List(List("NAME", "state"), List("Alabama", "01"), List("Alaska", "02"), List("Arizona", "04")))
      }
      else Left(new Exception(s"This didn't work bc blackbox didn't parse ${url}"))
    }
    //val seed = "passalong"
    val seed = "notgonnawork"
    val xx: Either[Exception, List[List[String]]] = blackBox(seed)
    def ff(i: List[List[String]]) = i.tail
    val yy = xx.map(ff)
    val zz = xx.fold(
      _  => throw new Exception("<need info here>"),
      i => i.tail)

诀窍在于 不从 Either 中获取状态名称/状态值对。它们应该放在里面。如果你愿意,你可以将 Either 类型转换成其他类型(例如 Option 通过丢弃你可能在左侧的任何东西),但不要破坏效果。应该有一些东西表明解码可能失败了;它可以是 EitherOptionTry 等。最终您将相应地处理左右大小写,但这应该尽可能晚地发生。

让我们来看下面这个简单的例子:

val x: Either[String, Int] = Right(42)
def f(i: Int) = i + 1

您可能会争辩说您需要从 Right 中取出 42,以便将其传递给 f。但那是不正确的。让我们重写这个例子:

val x: Either[String, Int] = someFunction()

现在呢?我们不知道值 x 中是 Left 还是 Right,所以我们不能 "get it out"。如果它是 Left,你会得到哪个整数? (如果你真的有一个整数值可以在这种情况下使用,那就足够了,我稍后会解决这个用例)

您需要做的是保持效果(在本例中为Either),并且您需要继续在该效果的上下文中工作。它表明您的程序中有一个点(在本例中 someFunction(),或在您的原始问题中解码)可能出错了。

所以如果你想将 f 应用于你的潜在整数,你需要 map 它的效果(我们可以这样做,因为 Either 是一个函子,但那是可能超出此答案范围的细节):

val x: Either[String, Int] = Right(42)
def f(i: Int) = i + 1

val y = x.map(value => f(value)) // Right(43)
val y = x.map(f) // shorter, point-free notation

val x: Either[String, Int] = someFunction()
def f(i: Int) = i + 1

// either a Left with some String, or a Right with some integer increased by 1
val y = x.map(f) 

然后,在计算链的最后,您可以处理 LeftRight 情况;例如,如果您正在处理 HTTP 请求,那么在 Left 的情况下,您可能 return 500,而在 Right return 的情况下,您可能是 200。

要解决前面提到的具有默认值的用例 - 如果您真的想这样做,请去掉 Left 并在这种情况下解析为某个值(例如 0),然后您可以使用fold:

def f(i: Int) = i + 1

// if x = Left, then z = 0
// if x = Right, then z = x + 1
val z = x.fold(_ => 0, i => i + 1)