2016-08-25 8 views
5

すべてのIterableをカスタムコードで拡張したいと思います。このため 私は次のように書いた:汎用クラスのサブタイプの暗黙のクラス

implicit class RichIterable[A, B <: Iterable[A]](b: B) { 
    def nonEmptyOpt: Option[B] = if (b.nonEmpty) Some(b) else None 
} 

、私は間違いなくそう

List(1, 2, 3).nonEmptyOpt 

ようIterableのサブクラスであることListに、このメソッドを使用する場合、私は

value nonEmptyOpt is not a member of List[Int] 
を取得

どうすれば解決できますか?私は一度つまずい

答えて

7

、コンパイラは(少なくとも上位境界を検索するために必要)、簡単にAが何であるかを把握する方法を知りません。

代わりに、トリックを使用せずにタイプ制約を再定義することでこれを行うことができます。基本的にBは、実際には、Iterableで囲まれたタイプのコンストラクタである必要があります。次に暗黙的なクラスは、B[A]から豊かなクラスへの変換です。 B[A]のパラメータを持つと、型コンストラクタBの引数であることが期待されるため、コンパイラはAを計算するのに役立ちます。

implicit class RichIterable[A, B[X] <: Iterable[X]](b: B[A]) { 
    def nonEmptyOpt: Option[B[A]] = if (b.nonEmpty) Some(b) else None 
} 

scala> List(1, 2, 3).nonEmptyOpt 
res0: Option[List[Int]] = Some(List(1, 2, 3)) 

scala> List.empty[Int].nonEmptyOpt 
res1: Option[List[Int]] = None 
+0

興味があるだけ、失われた/ "実存" 行くことによって何も変更されましたか? – jwvh

+0

@jwvhこれは、厳密に1つの型パラメータを持たないすべてのサブクラスに対してexactaの戻り値の型を失います( '' B、 '[A]それは、それらが拡張する 'Iterable'と同じです。いくつかの例は、 'Map [A​​、B]'または 'scala.xml.NodeSeq'です。たとえば、 'Map(1-> 2).nonEmptyOpt'は' Option [Map [...]] 'ではなく、Option [Iterable [...]]'となります。 – Kolmar

+0

@Kolmar、私は戻ってきました。 'B [X] ...'と 'B [_] ...'の間にあるので、 'Map()'ハンドリングの違いは分かりませんでした。 – jwvh

7

リトルトリック:

scala> implicit class RichIterable[A, B <: Iterable[A]](b: B with Iterable[A]) { 
| def nonEmptyOpt: Option[B] = if (b.nonEmpty) Some(b) else None 
| } 
defined class RichIterable 

scala> List(1,2,3).nonEmptyOpt 
res3: Option[List[Int]] = Some(List(1, 2, 3)) 

注パラメータにB with Iterable[A]。暗黙のデバッグ時にところで、それは(変更前)を明示的にそれらを適用しようとすることが時々役立ちます

:だから

scala> new RichIterable(List(1,2,3)).nonEmptyOpt 
<console>:15: error: inferred type arguments [Nothing,List[Int]] do not conform to class RichIterable's type parameter bounds [A,B <: Iterable[A]] 
      new RichIterable(List(1,2,3)).nonEmptyOpt 

、コンパイラはAの種類を考え出す苦労しています。型の改良は明らかにそれを助けます。それは必ずしも簡単Bから計算されていないため、唯一のタイプB <: Iterable[A]でパラメータを考えると

0

簡単な解決策は次のようになります。

implicit class RichIterable[A](b: Iterable[A]) { 
    def nonEmptyOpt: Option[Iterable[A]] = if (b.nonEmpty) Some(b) else None 
} 

scala> List(1,2,3).nonEmptyOpt 
res0: Option[Iterable[Int]] = Some(List(1, 2, 3)) 
+0

この方法の問題は、リストをIterableに変換したことです。 –

関連する問題