我正在阅读The Neophyte's Guide to Scala Part 8: Welcome to the Future,我感到困惑的一部分代码是:
import scala.util.Try
object CoffeeSync extends App {
// Some type aliases, just for getting more meaningful method signatures:
type CoffeeBeans = String
type GroundCoffee = String
case class Water(temperature: Int)
type Milk = String
type FrothedMilk = String
type Espresso = String
type Cappuccino = String
// dummy implementations of the individual steps:
def grind(beans: CoffeeBeans): GroundCoffee = s"ground coffee of $beans"
def heatWater(water: Water): Water = water.copy(temperature = 85)
def frothMilk(milk: Milk): FrothedMilk = s"frothed $milk"
def brew(coffee: GroundCoffee, heatedWater: Water): Espresso = "espresso"
def combine(espresso: Espresso, frothedMilk: FrothedMilk): Cappuccino = "cappuccino"
// going through these steps sequentially:
def prepareCappuccino(): Try[Cappuccino] = for {
ground <- Try(grind("arabica beans"))
water <- Try(heatWater(Water(25)))
espresso <- Try(brew(ground, water))
foam <- Try(frothMilk("milk"))
} yield {
combine(espresso, foam)
}
所有这一切都很好,但我感到困惑的是为什么Try
在For Fomprehension中起作用?
grind("arabica beans")
应返回GroundCoffee
类型的值,该类型是String
的类型别名。我知道For Comprehensions正在迭代一个Collection并且这被分配给一个值,并且Try(grind(...))
被视为1个元素的集合,因此ground
代表元素&#34; unwrapped&# 34。
但如果解释完成,那么当我执行以下操作时,我不会遇到编译错误:
// going through these steps sequentially:
def prepareCappuccino(): Try[Cappuccino] = for {
// Replaced Try with Seq
ground <- Seq(grind("arabica beans"))
water <- Try(heatWater(Water(25)))
espresso <- Try(brew(ground, water))
foam <- Try(frothMilk("milk"))
} yield {
combine(espresso, foam)
}
这给了我以下内容:
<console>:41: error: type mismatch;
found : scala.util.Try[CoffeeSync.Cappuccino]
(which expands to) scala.util.Try[String]
required: scala.collection.GenTraversableOnce[?]
water <- Try(heatWater(Water(25)))
^
<console>:40: error: type mismatch;
found : Seq[Nothing]
required: scala.util.Try[CoffeeSync.Cappuccino]
(which expands to) scala.util.Try[String]
ground <- Seq(grind("arabica beans"))
^
所以我想我真正要问的是为什么我的Seq[GroundCoffee]
会转换为Seq[Nothing]
?感谢您的帮助。
答案 0 :(得分:4)
我明白了For Comprehensions正在迭代一个集合
这不完全是事实。对于具有方法map
,flatmap
,foreach
,filter
或withFilter
的每种类型,可以使用理解。并且根据rules将每个for
块隐式转换为这些方法调用的链。例如:
for (p1 <-e1; p2 <-e2) yield (p1, p2)
相当于:
e1.flatMap(p1 => e2.map(p2 => (p1, p2)))
在你的情况下(我简化了一点,这足以得到编译错误,无论你实际返回什么):
def prepareCappuccino() = for {
ground <- Seq(grind("arabica beans"))
water <- Try(heatWater(Water(25)))
} yield water
您的代码将被翻译为:
def prepareCappuccino() =
Seq(grind("arabica beans"))
.flatMap(ground => Try(heatWater(Water(25))).map(water => water))
这里有一个问题:flatMap
中的方法Seq
有签名:
def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That
需要f
- 类型为A => GenTraversableOnce[B]
的函数。但实际上你传递的是A => Try[B]
类型的函数,因为Try[B]
不是GenTraversableOnce[B]
的子类型,所以你得到了编译错误。
因此,您无法以这种方式将Try
和Seq
或其他集合合并到块中,除非您有从Try
到GenTraversableOnce
的隐式转换。例如,您可以组合Option
和集合:
val r = for {
x <- Seq(3)
y <- Option(5)
} yield (x,y)
此代码编译成功,因为Option
对象中定义了隐式转换:
implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList