简体   繁体   中英

How to define a type parameter that can be either a type or a container for that type?

I want to define a class like this:

class MyClass[C,X](
  val states: C,
  val transform: C => X
)

But X can only be equals to C or a container for C , like List[C] or Set[C] -- it does not make sense for the problem at hand if X is defines as anything else.

Is there a way to impose this restriction in Scala?

You can try

import scala.language.higherKinds

class MyClass[C, F[_]](
                       val states: C,
                       val transform: C => F[C]
                      )

type Id[A] = A

new MyClass[Int, Set](1, Set(_)) // Set[Int] is a container for Int
new MyClass[String, List]("a", List(_)) // List[String] is a container for String
new MyClass[Boolean, Id](true, x => x) // Id[Boolean] is Boolean itself

EDIT

Assuming XY-problem. Now this posting has two answers:

  1. The cumbersome solution that relies on subclassing
  2. Typeclass-based solution that probably solves the actual X

Subclassing-solution

If you run

List(List(0), Set(0))

in the interpreter, you will see that List and Set unify only at Iterable .

Thus, the most specific restriction you can make is:

import scala.language.higherKinds

class MyClass[C, Container[X] <: collection.immutable.Iterable[X]] (
  val states: C,
  val transform: C => Container[C]
)

I wouldn't advise to make it this generic though. If in doubt, take List first, generalize later only if it's actually necessary.


Typeclass-solution

From your comment, it looks as if it's an XY-problem, and what you actually want is a type constructor F with an appropriate type-class.

Define your type-class first, for example with the constraint that one sholud be able to iterate through F :

trait MyTC[F[_]] {
  def hasIterator[X](fx: F[X]): Iterator[X]
}

Then define MyClass for arbitrary F for which there is an instance of the typeclass:

class MyClass[X, F[_] : MyTC] {
  val states: X
  val transform: X => F[X]
}

Then simply define instances of MyTC for List , Set , or Id :

implicit object ListMyTC extends MyTC[List] {
  def hasIterator[X](lx: List[X]): Iterator[X] = lx.iterator
}

type Id[X] = X
implicit object ListMyTC extends MyTC[Id] {
  def hasIterator[X](x: X): Iterator[X] = Iterator(x)
}

And then you can use MyClass with List s or with Id :

val m1 = new MyClass[Int, List] { ... } // Integer states, transforms to List
val m2 = new MyClass[String, Id] { ... } // String-labeled states, transforms to other `String`

etc.

The idea is essentially to replace your attempt to extensionally enumerate all the container types for which you think your construction might work by an intensional definition, that accepts any F that can prove that it satisfies all the requirements in MyTC by providing an instance of MyTC[F] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM