2016-11-29 42 views
3

假设我有类似功能的类型,例如获取“最具体”的输入类型

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> 

B ut我想要的是zipFuncs(f2, f3)根本不能编译。由于String不是Option[Int]的子类型,并且Option[Int]不是String的子类型,因此无法构建res3的输入值。

我没有创建一个类型类:

// 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) 
} 

这实现上述目标,但有一个非常恼人的问题。 IntelliJ将突出显示有效组合作为错误,推断出“最具体类型(A)”实际上是Nothing,实际上它是一个实际值。 Here's the actual issue in practice

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

回答

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] 
+0

这就是我一直在寻找的行为。我将不得不在我的实际项目中尝试这种方法! – Dylan

4

可以实现,使用generalized type constraints

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)也可以。如果你确实有这样的要求,我看不出有什么其他的方法比你做的一些implicit type gymnastics类似。

编辑:请参阅Eduardo的答案,以实现这一点的巧妙方法。

是的,我也有很多情况,当IntelliJ将事情看成错误时,事实上并非如此。我知道这很乏味,但除了报告问题和等待之外,我没有办法解决这种情况。

+0

干杯的响应。很高兴知道我不是唯一有荧光笔问题的人,但是很遗憾,这是一个问题。我确实有“这个要求”,这就是为什么我最终接受Eduardo的答案,但+1取消了时间 – Dylan

+0

没有probs。如果有人对广义类型约束没有任何线索,我想我的答案在查看@Eduardo如何使用它们(以及一些额外的技巧)使整个事情工作之前更容易看到这个想法:) – slouc