我有这样的理解力:
for {
(value1: String, value2: String, value3: String) <- getConfigs(args)
// more stuff using those values
}
getConfigs
返回一个Either[Throwable, (Seq[String], String, String)]
,当我尝试编译时出现此错误:
value withFilter is not a member of Either[Throwable,(Seq[String], String, String)]
如何在理解中使用此方法(返回Either
)?
答案 0 :(得分:3)
赞:
for {
tuple <- getConfigs()
} println(tuple)
开个玩笑,我认为这是一个有趣的问题,但它的名字被误命名了。
问题(参见上文)不是因为无法理解,而是无法在Either
内进行for理解内的模式匹配。
有documentation how for comprehensions are translated,但它们不能涵盖所有情况。据我所知,这里并没有涵盖。因此,我在“ Scala编程”(第二版)实例中进行了查找(因为这是我站在枯树旁的那个版本)。
有一个子章节“生成器中的翻译模式”,这就是如上所述的问题。它列出了两种情况:
正是我们的情况:
for ((x1, …, xn) <- expr1) yield expr2
应翻译为expr1.map { case (x1, …, xn) => expr2)
。
当您选择代码并执行“理解的Desugar”操作时,IntelliJ正是这样做的。好极了!
…但这使它在我眼中变得更加奇怪,因为经过解密的代码实际上运行没问题。
因此,这种情况是(imho)匹配的情况,但不是正在发生的情况。至少不是我们观察到的。嗯?!
for (pat <- expr1) yield expr2
翻译为
expr1 withFilter {
case pat => true
case _ => false
} map {
case pat => expr2
}
现在有一个withFilter
方法!
这种情况完全说明了错误消息,以及为什么无法在Either
中进行模式匹配。
本章最终指的是the scala language specification(尽管是较旧的),这是我现在要停止的地方。
抱歉,我不能完全回答这个问题,但希望我能在这里暗示问题根源是什么。
那么Either
为何有问题,却没有提出withFilter
和Try
的{{1}}方法呢?
由于Option
从“容器”(可能是“全部”)中删除了元素,因此我们需要一些表示“空容器”的东西。
这对于filter
很容易,而显然是Option
。也很容易例如None
。对于List
来说并不是那么容易,因为有多个Try
,每个人都可以容纳一个特定的异常。但是,这个地方有多个失败之处:
Failure
和NoSuchElementException
,这就是为什么UnsupportedOperationException
运行,而Try[X]
没有运行的原因。
这几乎是同一件事,但并非完全相同。 Either[Throwable, X]
知道Try
是Left
,库作者可以从中受益。
然而,在Throwable
(现在是右偏)上,“空”情况是Either
情况;这是通用的。因此,用户可以确定它是哪种类型,因此库作者无法为每种可能的左值选择通用实例。
我认为这就是为什么Left
不提供现成的Either
以及表达失败的原因。
顺便说一句。
withFilter
案例是可行的,因为它在调用堆栈上抛出了expr1.map { case (x1, …, xn) => expr2) }
并引起了问题的恐慌,而问题本身可能是一个更大的问题。
哦,对于那些足够勇敢的人:到目前为止,我还没有使用过“ Monad”一词,因为Scala没有用于它的数据结构,但是理解就可以了。但是也许引用不会对您造成伤害:Additive Monads拥有这个“零”值,这正是MatchError
在这里遗漏的东西,也是我试图在“直觉”部分赋予一些含义的东西。
答案 1 :(得分:2)
我想您希望仅在值为Right时才运行循环。如果是Left,则不应运行。这可以很容易实现:
for {
(value1, value2, value3) <- getConfigs(args).right.toOption
// more stuff using those values
}
边注::我不知道您的确切用例是什么,但是scala.util.Try
更适合您遇到结果或失败(异常)的情况。
只需写Try { /*some code that may throw an exception*/ }
,您将拥有Success(/*the result*/)
或Failure(/*the caught exception*/)
。
如果您的getConfigs
方法返回的是Try
而不是Either
,那么您的上述操作无需任何更改即可工作。
答案 2 :(得分:1)
我认为您可能会感到惊讶的部分是,Scala编译器会发出此错误,因为您在原位解构了元组。令人惊讶的是,这迫使编译器检查withFilter
方法,因为它看起来像编译器那样隐式检查容器内值的类型,并使用withFilter
实现对值的检查。如果您将代码编写为
for {
tmp <- getConfigs(args)
(value1: Seq[String], value2: String, value3: String) = tmp
// more stuff using those values
}
它应该编译没有错误。
答案 3 :(得分:1)
您可以使用Oleg的better-monadic-for编译器插件来完成此操作:
build.sbt
:
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.2.4")
然后:
object Test {
def getConfigs: Either[Throwable, (String, String, String)] = Right(("a", "b", "c"))
def main(args: Array[String]): Unit = {
val res = for {
(fst, snd, third) <- getConfigs
} yield fst
res.foreach(println)
}
}
收益:
a
之所以有用,是因为该插件在删除关键字时删除了不必要的withFilter
和unchecked
并使用了.map
调用。因此,我们得到:
val res: Either[Throwable, String] =
getConfigs
.map[String](((x$1: (String, String, String)) => x$1 match {
case (_1: String, _2: String, _3: String)
(String, String, String)((fst @ _), (snd @ _), (third @ _)) => fst
}));