score:6

Accepted answer

as you might know, if you have a parameter of type foo[a], then you can make the method generic in just a:

def dostuff[a](p: foo[a]): a = ???

since that might not always be the case, we can try to use an implicit parameter that can express the relationship between a and b. since we can't only apply some of the generic parameters to a method call (generic parameter inference is all or nothing), we have to split this into 2 calls. this is an option:

case class stuff[b <: foo[_]]() {
    def get[a](p: param)(implicit ev: b => foo[a]): a = ???
}

you can check the types in the repl:

:t stuff[intfoo].get(new param) //int
:t stuff[stringfoo].get(new param) //string

another option along the same lines, but using an anonymous class, is:

def stuff[b <: foo[_]] = new { 
    def apply[a](p: param)(implicit ev: b <:< foo[a]): a = ??? 
}

:t stuff[intfoo](new param) //int

here, i've used apply in stead of get, so you can apply the method more naturally. also, as suggested in your comment, here i've used <:< in the evidence type. for those looking to learn more about this type of generalized type constraint, you can read more here.

you might also consider using abstract type members instead of generic parameters here. when struggling with generic type inference, this often provides an elegant solution. you can read more about abstract type members and their relationship to generics here.

score:7

another option is to use type members:

sealed trait foo {
  type value
  def value: value
}

case class stringfoo(value: string) extends foo { type value = string }
case class intfoo(value: int) extends foo { type value = int }

def dostuff[b <: foo](p: any): b#value = ???

// hypothetical invocation
val i: int = dostuff[intfoo](param)
val s: string = dostuff[stringfoo](param)

note that both solutions mainly work around the syntactic restriction in scala, that you cannot fix one type parameter of a list and have the compiler infer the other.


Related Query

More Query from same tag