2015-07-21 102 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