maks
maks

Reputation: 6006

Restrict function generic type parameters

Having following structure:

trait ParentType
trait ChildType extends ParentType

case class FooBar[+T <: ParentType](s: String, data: T)
object FooBar {
  def apply[T <: ChildType](i: Int, data: T): FooBar[T] = new FooBar(s"$i", data)
}
case class Foo(a: Int) extends ParentType
case class Bar(s: String) extends ChildType


def function1[T <: ParentType](obj: FooBar[T]): Unit = ()
def function2[T <: ChildType](obj: FooBar[T]): Unit = ()

Is possible(and in what way) to change the signature(with generics declaration) of function1 and function2 such that function1 can only except parameters of FooBar with type T that is direct descendant of ParentType(and prohibit any T that is direct descendant of ChildType) and function2 in another way around - except only parameters with T that are direct descendants of ChildType and prohibit all ParentType T's

In other words this shouldn't compile

function1(FooBar("a", Bar("1"))) //because Bar is not direct descendant of ParentType

Upvotes: 0

Views: 92

Answers (2)

maks
maks

Reputation: 6006

With help of @LuisMiguelMejíaSuárez (see his comments to the question) I managed to achieve what I wanted. So, taking into account as well that I need context bound for Arbitrary from scalacheck to be in these functions declarations, Luis suggestion to use shapless(in particular I found out that <:!< can solve my problem) helped

    import shapless._

    def function1[T <: ParentType](obj: FooBar[T])(implicit: arb: Arbitrary[T], ev: T <:!< ChildType): Unit = ()
    def function2[T <: ChildType : Arbitrary](obj: FooBar[T]): Unit = ()

Unfortunately I didn't manage to achieve the override of function1(just with different ev types), but having those functions with different names I can reuse function1 from function2(I need just to call function1 with explicit parameter for <:!< instance which is shapeless.nsub).

So with this solution any ChildType instance can't be applied to function1(at least as is) because such application results in

ambiguous implicit values:
 both method nsubAmbig1 in package shapeless of type [A, B >: A]A <:!< B
 and method nsubAmbig2 in package shapeless of type [A, B >: A]A <:!< B
 match expected type Bar <:!< ChildType

I guess this is what was designed behind usage of <:!< type(but if explicitly to specify an instance of <:!< it will work)

Here is a link to scastie worksheet to try out the problem

Upvotes: 0

Dima
Dima

Reputation: 40510

The question does not make very much sense, I am afraid, but to answer what you wrote in comments:

I want function2 to accept "containers" FooBars of Mammals only

So, it should be def function2(m: Foobar[Mammal])

and function1 - all other group types of animals(but not mammals)

This is a weird requirement. If you are sure you need this (I really, really doubt you actually do), I suggest something like this:

   trait NotMammal extends Animal
   class Fish extends NotMammal
   class Reptile extends NotMammal

   ... 

   def funcction1(n: Foobar[NotMammal]) = ???

Upvotes: 1

Related Questions