Scala constructor abstraction

In Scala, the following are possible:

scala> val l = List l: scala.collection.immutable.List.type = scala.collection.immutable.List$@7960c21a scala> l ( 1, 2, 3 ) res0: List[Int] = List(1, 2, 3) 

In other words, Scala has a higher order polymorphism. I would like to use a higher order polymorphism to do the following.

 sealed abstract class A { def eval () : A } case class A0 () extends A { ... } case class A1 ( a : A ) extends A { ... } case class A2 ( a : A, b : A ) extends A { ... } .... 

So, I have a bunch of case classes, subclasses of A , whose constructors do not necessarily take the same number of arguments. I would also like to have a β€œgeneric” case class, something like this:

 case class ApplyA ( c : ???, l : List [ A ] ) extends A { def eval () : A = { ??? } } 

The idea is that ApplyA takes as its first argument a constructor for what is a subtype of A , and a list of arguments. Then, the eval method creates a possible class with a constructor (i.e., the List has the correct length) and returns it (this corresponds to l ( 1, 2, 3) in the List example above). What will be the argument type of the first constructor for ApplyA ?

This should be possible with a higher polymorphism, but I could not figure out how to do this. I know that I can do this even without using a higher order polymorphism by simply wrapping the constructors in functions and then passing those functions as the first argument to the constructor for ApplyA , but I would like to understand how to use the higher order polymorphism directly.

+7
source share
2 answers

The problem is that the List example is not at all related to a higher order polymorphism. List.apply just accepts a variable number of parameters:

 def apply(xs: A*) 

Higher order polymorphism includes methods or types that accept type constructors as type parameters, for example

 def fmap[F[_], A](x: F[A]): F[B] 

No, you cannot do this using higher order polymorphism.

+9
source

@alexey_r is perfectly correct that your List example does not include higher order polymorphism. But if you are ready to use some heavy level-type artillery , you can abstract from the artery of your A{0,1,2} constructors to get something that looks very close to what you are asking for.

First of all, it should be noted that, as written, your "common" class cannot be implemented,

 case class ApplyA(c : ???, l : List[A]) ... 

because there is no interdependent relationship of time between constructor constructor c and list length l . We can fix this problem by replacing List with an HList and helping to convert from regular functions with arbitrary arity to functions with a single HList argument.

 import shapeless.HList._ import shapeless.Functions._ sealed abstract class A { def eval() : A } case class A0 () extends A { def eval() = this } case class A1 ( a : A ) extends A { def eval() = this } case class A2 ( a : A, b : A ) extends A { def eval() = this } case class ApplyA[C, L <: HList, HF](c : C, l : L) (implicit hl : FnHListerAux[C, HF], ev : HF <:< (L => A)) extends A { def eval () : A = hl(c)(l) } val a : A = A0() val a0 = ApplyA(A0.apply _, HNil) val a1 = ApplyA(A1.apply _, a :: HNil) val a2 = ApplyA(A2.apply _, a :: a :: HNil) 

The implicit hl : FnHListerAux[C, HF] argument provides conversion from your constructor, regardless of arity, to a function from a single HList argument. And the implicit argument ev : HF <:< (L => A) indicates that the length of the provided argument to the HList constructor is the correct length (and type FWIW, but this is hardly relevant in this example).

+11
source

All Articles