【问题标题】:Can I get Scala to infer the Option type here?我可以让 Scala 在这里推断 Option 类型吗?
【发布时间】:2015-07-21 18:44:35
【问题描述】:

我想调用一个通用函数f[X](...),在我的例子中X恰好是Option[Y]。我尝试将Some[...]None 都传递给函数需要X,但Scala 坚持X 的类型为Some[Y]

  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]] 之类的繁琐类型会大大降低我的代码的可读性。

有没有办法让 Scala 推断出类型,或者避免复制粘贴整个类型?

【问题讨论】:

  • 如果使用Option(Map.empty[A, B])作为foldLeft的起始累加器,可以省略类型。

标签: scala coding-style type-inference


【解决方案1】:

当您使用Option(Map.empty[A, B]) 作为foldLeft 的起始值时,Scala 将推断出我在 cmets 中所写的正确类型(以及他的回答中的 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

【讨论】:

  • 我想评论一下,这种技术通常用于将F[G[_]] 的实例翻转到G[F[_]]。当然有一些规则,但sequence 会有效地将你的对象翻过来!经常出人意料的事情。
【解决方案2】:

如果您的目标是提高可读性,并从编译器那里获得帮助来推断类型,请尝试:

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

【讨论】:

  • 谢谢(还有 Peter Neyens),这正是我想要的(是的,序列解决方案似乎更好)。我自己不知道如何找到apply 函数。它似乎没有出现在API ref 中,即使它在源代码中,甚至还有一个文档字符串。
  • Option.apply 所做的实际上只是在幕后构建一个Some(..)。即它只是一个创建Some(..)None 的工厂方法。需要注意的关键是返回类型是超类型Option,而不是Some。在您原来的折叠中,编译器推断您的累加器是Some 类型。当折叠返回None 时,scalac 抱怨NoneSome 类型(累加器的类型)发生冲突。这就是为什么您必须提供类型参数以将Some 的类型扩大到Option
猜你喜欢
  • 2011-03-19
  • 2017-06-22
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 2014-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多