2015-09-05 65 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'。 –