Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
コンパイルが通らない
abstract class C
case class C1() extends C
case class C2() extends C
trait Service[T <: C] {
def getFoo(): Option[T]
}
object Service {
// found : List[Option[Any]]
// required: List[Option[T]]
def getAllFoos(services: List[Service[_]]): List[Option[C]] = {
services.map(_.getFoo())
}
}

xuwei-k commented Sep 22, 2014

def getAllFoos[A <: C](services: List[Service[A]]): List[Option[C]]

(他にも解決方法あるかも)

  def getAllFoos[A <: C](services: List[Service[A]]): List[Option[C]]

これだと

object C1Service extends Service[C1] { def getFoo(): Option[C1] = None }
object C2Service extends Service[C2] { def getFoo(): Option[C2] = None }

があった時、

Service.getAllFoos(List(C1Service, C2Service))

が上手く行かないっぽいですね。
どうすればいいんだろう?

一応こうすればいけますが、意図が変わってる可能性ありますね。

abstract class C
case class C1() extends C
case class C2() extends C

trait Service[+T <: C] {
  def getFoo(): Option[T]
}

object Service {
  def getAllFoos(services: List[Service[C]]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

object C1Service extends Service[C1] { def getFoo(): Option[C1] = None }
object C2Service extends Service[C2] { def getFoo(): Option[C2] = None }

Service.getAllFoos(List(C1Service, C2Service))

もしくはこうか。

abstract class C
case class C1() extends C
case class C2() extends C

trait Service {
  type T <: C
  def getFoo(): Option[T]
}

object Service {
  def getAllFoos(services: List[Service]): List[Option[C]] = {
    services.map(_.getFoo())
  }
}

object C1Service extends Service { type T = C1; def getFoo(): Option[C1] = None }
object C2Service extends Service { type T = C2; def getFoo(): Option[C2] = None }

Service.getAllFoos(List(C1Service, C2Service))
Owner

k4200 commented Sep 22, 2014

@xuwei-k @gakuzzzz
どうもありがとうございます。

def getAllFoos[A <: C](services: List[Service[A]]): List[Option[C]]

メソッドにタイプパラメーター渡すことが出来るのを忘れてました・・・

実際には、がくぞうさんの出してくれた例の通り、Serviceは継承して使うことを想定しています。
子供のobjectで type T=C1 みたいに指定する必要があるんですね。これで試してみます。

ポイントは trait Service[T <: C] にした場合、 Service[C]Service[C1]Service[C2] が全然関係ない型になってしまうので、統一的に扱えないって所ですね。

これを trait Service[+T <: C] とすれば、Service[C1]Service[C2]Service[C] のサブ型と見做せるようになるので Service[C] として統一的に扱えるようになります。

また、型引数を止めて、抽象メンバ型でやれば、Service という型は一つになるので、こちらも Service で統一的に扱えるようになります。 type T = C1 が必要なのはこの場合ですね。

共変を使うか、抽象メンバ型を使うか、の判断基準としては、「C1に特化したServiceの型を扱いたいかどうか」になるかと思います。

共変を使うパターンであれば以下の様な、Cの各実装クラスに特化したサービスを型で表す事ができますが、抽象メンバ型を使う場合は、全てが Service 型になってしまうのでC1に特化した型を表現するのが難しくなります。

def foo(s: Service[C1]): C1 = ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment