Search code examples
scalaimplicittype-constraints

Using implicit type evidence to satisfy type constraint


I'm trying to make a builder with a number of unconstrained types that can build a class with constrained types. How can I use implicit evidence of type boundaries to satisfy the type constraints?

For instance say I want to build a class:

class NameClass[N <: String](name: N)

And I use a builder that looks like this:

class NameClassBuilder[N](n: N) {
  def name[NewN <: String](name: NewN) = new NameClassBuilder[NewN](name)

  def build(implicit ev: N <:< String) : NameClass[N] = new NameClass[N](n)
}

object NameClassBuilder {
  def builder : NameClassBuilder[Unit] = new NameClassBuilder[Unit]()
}

The idea is that you can start out with a dummy type/value and then update the type as you add fields. By using evidence that the types are correct, you can only build it when the types make sense.

The problem is it doesn't compile, on the grounds that the N from NameClassBuilder does not satisfy the type constraint N <: String in NameClass, even though I've included evidence that it does.

error: type arguments [N] do not conform to class NameClass's type parameter bounds [N <: String]
         def build(implicit ev: N <:< String) : NameClass[N] = new NameClass[N](n)

Is there any way to use the evidence to satisfy this constraint?


Solution

  • You can return NameClass[N with String] instead. N with String <: String for any N, and if actually N <: String then N with String = N. The N <:< String you have in build is required to cast n: N to N with String.

    def build(implicit ev: N <:< String): NameClass[N with String] = {
      type F[+X] = N with X
      new NameClass(ev.liftCo[F](n))
    }
    

    Complete example on Scastie