2015-07-21 63 views
2

我要調用一個泛型函數f[X](...),並在我的情況X恰好是Option[Y]。我試圖通過這兩個Some[...]None當函數需要X,但斯卡拉堅持XSome[Y]型。我可以讓Scala在此推斷Option類型嗎?

def flattenOptionMap[A, B](input : Map[A, Option[B]]) : Option[Map[A, B]] = { 
    input.foldLeft[Option[Map[A,B]]] (Some(Map.empty)) { 
     case (_, (_, None)) => None 
     case (None, (_, _)) => None 
     case (Some(acc), (key, Some(value))) => Some(acc + (key -> value)) 
    } 
    } 

在這個例子中,我必須明確指定Option[Map[A,B]]應作爲一般類型foldLeft。所有必要的類型信息已經包含在上下文中,並且在我看來,輸入繁瑣的類型(比如Option[Map[A,B]])往往會大大降低我的代碼的可讀性。

有沒有辦法讓斯卡拉畢竟推斷類型,否則將避免複製粘貼整個類型?

+3

如果您使用'Option(Map.empty [A,B])'作爲'foldLeft'的開始累加器,則可以省略該類型。 –

回答

5

當您使用Option(Map.empty[A, B])作爲您的foldLeft的起始值時,Scala會根據我在評論中寫的正確類型(以及他在答案中的beefyhalo)推斷出正確的類型。

我想補充一點,如果你打算使用Scalaz,你可以使用sequence函數。

import scalaz._ 
import Scalaz._ 

val mapSome = Map(1 -> Some("a"), 2 -> Some("b")) 
val mapNone = Map(1 -> Some("a"), 2 -> Some("b"), 3 -> None) 

mapSome.sequence 
flattenOptionMap(mapSome) 
// Option[Map[Int,String]] = Some(Map(1 -> a, 2 -> b)) 

mapNone.sequence 
flattenOptionMap(mapNone) 
// Option[Map[Int,String]] = None 
+4

我想評論說,這種技術通常用於將F [G [_]]的實例翻轉爲' G [F [_]]'。當然有一些規則,但是'sequence'將有效地將你的對象從裏面取出!出乎意料地經常出現的東西。 – beefyhalo

4

如果你的目標是提高可讀性,並從編譯器推斷類型獲得幫助,請嘗試:

def flattenOptionMap[A, B](input: Map[A, Option[B]]) = 
    input.foldLeft(Option(Map.empty[A, B])) { 
    case (_, (_, None))     => None 
    case (None, (_, _))     => None 
    case (Some(acc), (key, Some(value))) => Some(acc + (key -> value)) 
    } 

甚至更​​好:

def flattenOptionMap[A, B](input: Map[A, Option[B]]) = 
    input.foldLeft(Option(Map.empty[A, B])) { 
    case (Some(acc), (key, Some(value))) => Some(acc + (key -> value)) 
    case _        => None 
    } 

或者甚至更好(以我的意見):Peter Neyens的回答。使用Traverse實例中的sequence

+0

謝謝你(和Peter Neyens),這正是我正在尋找的東西(是的,序列解決方案似乎更好)。我不知道如何自己找到'apply'函數。它似乎沒有出現在[API ref](http://www.scala-lang.org/api/2.11.5/index.html#scala.Option)中,即使它在源代碼中它甚至有一個文檔字符串。 – cib

+3

什麼'Option.apply'正在做的只是在幕後構建一個「Some(..)」。即它只是一個創建「Some(..)」或「None」的工廠方法。關鍵要注意的是,返回類型是超類型「Option」,而不是「Some」。在你的原始文件中,編譯器推斷你的累加器是「Some」類型的。當fold返回'None'時,scalac會抱怨'None'與'Some'類型(累加器的類型)衝突。這就是爲什麼你必須提供類型參數來將'Some'類型擴展爲'Option' – beefyhalo

相關問題