获得最具体的"输入类型

时间:2016-11-29 20:25:04

标签: scala intellij-idea types

假设我有类似函数的类型,例如

trait Parser[-Context, +Out]

并且我希望能够组合多个解析器,使得组合的Context将是组合解析器中最具体的类型'上下文。例如:

Parser[Any, Int] + Parser[String, Long] = Parser[String, (Int, Long)]
Parser[String, Int] + Parser[Any, Long] = Parser[String, (Int, Long)]
Parser[Option[Int], Foo] + Parser[Some[Int], Bar] = Parser[Some[Int], (Foo, Bar)]
Parser[String, Foo] + Parser[Int, Bar] = <should be a compile error>

为了更具体地说明这个例子,假设我有一个像

这样的函数组合器
def zipFuncs[A, B1, B2](f1: A => B1, f2: A => B2): A => (B1, B2) = {
  a => (f1(a), f2(a))
}

和一些功能,如

val f1 = { a: Any => 123 }
val f2 = { a: String => 123 }
val f3 = { a: Option[Int] => 123 }

现在我可以做到

> zipFuncs(f1, f2)
res1: String => (Int, Int) = <function>

> zipFuncs(f1, f3)
res2: Option[Int] => (Int, Int) = <function>

> zipFuncs(f2, f3)
res3: Option[Int] with String => (Int, Int) = <function1>

但我想要的是zipFuncs(f2, f3)根本不能编译。由于String不是Option[Int]的子类型,Option[Int]不是String的子类型,因此无法为{{1}构造输入值}}。

我确实创建了一个类型类:

res3

这实现了上述目标,但带来了一个非常烦人的问题。 IntelliJ会将有效组合突出显示为错误,并推断出最具体的类型(// this says type `T` is the most specific type between `T1` and `T2` sealed trait MostSpecificType[T, T1, T2] extends (T => (T1, T2)) // implementation of `object MostSpecificType` omitted def zipFuncs[A, A1, A2, B1, B2](f1: A1 => B1, f2: A2 => B2)( implicit mst: MostSpecificType[A, A1, A2] ): A => (B1, B2) = { a: A => val (a1, a2) = mst(a) f1(a1) -> f2(a2) } )&#34;实际上它是A,实际上它是一个真正的价值。 Here's the actual issue in practice

突出显示的问题肯定是IntelliJ中的一个错误,谷歌搜索似乎意味着各种重置/缓存擦除/等应该修复它(它没有)。无论责任如何,我都希望找到一种既满足我原始要求的替代方法,也不会混淆IntelliJ。

2 个答案:

答案 0 :(得分:4)

您可以使用https://developers.google.com/identity/protocols/OAuth2WebServer来实现这一目标:

def zipFuncs[A1, A2, B1, B2](f1: A1 => B1, f2: A2 => B2)
                            (implicit ev: A2 <:< A1): A2 => (B1, B2) = {
  a => (f1(a), f2(a))
}

val f1 = { a: Any => 123 }
val f2 = { a: String => 123 }
val f3 = { a: Option[Int] => 123 }

zipFuncs(f1, f2) // works
zipFuncs(f1, f3) // works
zipFuncs(f2, f3) // cannot prove that Option[Int] <:< String

但是,这需要第二个函数在输入参数中使用比第一个更具体的类型。除非您也希望zipFuncs(f2, f1)也能工作,否则这没关系。如果你确实有这个要求,我除了做一些generalized type constraints类似于你已经做过的事情之外,我没有看到任何其他方式。

编辑:请参阅Eduardo的答案,了解实现这一目标的巧妙方法。

是的,当IntelliJ将某些内容视为错误时,我也遇到过许多情况,实际上并非如此。我知道这很乏味但除了报告问题和等待之外我还没有办法解决问题。

答案 1 :(得分:2)

如果你希望它只在其中一种类型是另一种类型的子类型时才能工作,那么你可以这样做:

def Zip[A,X,Y](f: A => X, g: A => Y): A => (X,Y) = a => (f(a), g(a))

implicit class ZipOps[A,X](val f: A => X) extends AnyVal {

  def zip[A0, Y](g: A0 => Y)(implicit ev: A0 <:< A): A0 => (X,Y) = 
    Zip({a: A0 => f(a)},g)

  def zip[A0 >: A, Y](g: A0 => Y): A => (X,Y) = 
    Zip(f,g)

}

val f1: Any => Int = { a: Any => 123 }
val f2: String => Int = { a: String => 123 }
val f3: Option[Int] => Int = { a: Option[Int] => 123 }

val x1 = f1 zip f2 // works
val x1swap = f2 zip f1 // works
val x2 = f1 zip f3 // works
val x3 = f2 zip f3 // cannot prove that Option[Int] <:< String
val x3swap = f3 zip f2 // cannot prove that String <:< Option[Int]