2016-11-29 3 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> 

は、より具体的に例を置くために、私は今、私は

を行うことができます

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

よう

def zipFuncs[A, B1, B2](f1: A => B1, f2: A => B2): A => (B1, B2) = { 
    a => (f1(a), f2(a)) 
} 

のような関数合成といくつかの機能を持っていると仮定

> 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私が望むのは、zipFuncs(f2, f3)がまったくコンパイルされないということです。 StringOption[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のではバグであり、Googleの検索は、さまざまなリセット/キャッシュは、/ etcワイプすることを暗示しているようだ、それを修正する必要があります。その責任にかかわらず、私は元の要件を満たし、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 

しかし、これは最初のものよりも入力パラメータに、より具体的な型を使用する第2の機能を必要とします。 zipFuncs(f2, f1)も機能しない限り、これは問題ありません。そのような要件がある場合は、私がすでに行っているのと同様のものをimplicit type gymnasticsとする以外の方法はありません。

EDIT:これを達成するために巧妙なトリックのためのエドゥアルドの回答を参照してください。

そして、はい、私も実際にはそれがないときにIntelliJのは、エラーとして何かを見ている多くの状況がありました。面倒だと分かっていますが、問題を報告して待つ以外の状況を修正する方法はありません。

+0

乾杯を。蛍光灯の問題を抱える唯一の人ではないことを知ってうれしいですが、それはまったく問題だと悲しんでいます。私は最終的にエドゥアルドの答えを受け入れた理由である "その要件"を持っていたが、時間をとって+1 +1 – Dylan

+0

probs。もし誰かが一般化された型の制約についての手がかりがないなら、私の答えは、@Eduardoがそれらを(余分なトリックを加えて)どのように使用しているかチェックする前に、アイデアを見るのが少し簡単だと思います。 – slouc

関連する問題