2015-09-05 78 views
2

我读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作品中对于Fomprehension?

grind("arabica beans")应该返回类型GroundCoffee的值,它是String的类型别名。我知道For Comprehensions遍历一个Collection,并且它被赋值为一个值,并且该Try(grind(...))被视为一个元素的Collection,因此ground表示元素“unwrapped”。

但是,如果这种解释是完全的,那么我就不会当我做下面的得到一个编译错误:

// 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]感谢您的帮助。

回答

4

我得到的有关内涵是通过收集

迭代这不是完全的真理。为了理解可以用于具有方法map,flatmap,foreach,filterwithFilter的每种类型。并且每个for块都根据rules隐式转换为这些方法调用的链。例如:

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)) 

在这里,你有一个问题:在Seq方法flatMap有签名:

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]的子类型,所以您有编译错误。

那么你就不能以同样以这种方式,除非你有从TryGenTraversableOnce一些隐式转换结合TrySeq或其他集合块。例如,您可以结合Option和集合:

val r = for { 
    x <- Seq(3) 
    y <- Option(5) 
} yield (x,y) 

这段代码编译成功,因为在Option对象中定义的隐式转换:在`Try`

implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList 
+0

还有一个'toOption'方法你可以用它来完成这项工作。 “Try”实际上不是一个Monad,它可以防止设计级别之后的构图。详情请浏览:https://gist.github.com/ms-tg/6222775。 – flavian

+0

@ ka4eli,谢谢您的详尽解答!正确回答我的问题。我认为最好总是认为理解只是语法上的'map'和'flatMap'。 –