I have the following Types
sealed trait Types
object Types {
case class A(value: Int) extends Types
case class B(value: String, flag: Boolean) extends Types
case class L(value: Types, n: Int) extends Types
}
I want to have a generate
method that will generate an output based on the input type, and the output might have different types. I tried to use the Aux pattern and for type A
and type B
the values are generated.
trait Generate[In] {
type Out
def generate(in: In): Out
}
object Generate {
type Aux[I, O] = Generate[I] {type Out = O}
implicit object AGenerate extends Generate[Types.A] {
override type Out = Int
override def generate(a: Types.A): Out = a.value
}
implicit object BGenerate extends Generate[Types.B] {
override type Out = String
override def generate(b: Types.B): Out = if (b.flag) b.value else "nope"
}
// implicit def lGenerate[T, O](implicit aux: Aux[T, O]): Generate[Types.L] = new Generate[Types.L] {
// override type Out = List[O]
//
// override def generate(in: Types.L): Out = List.fill(in.n)(aux.generate(in.value))
// }
implicit class RichGenerator[I](in: I) {
def generate[O](implicit aux: Aux[I, O]): O = aux.generate(in)
}
}
object Test extends App {
import Types._
import Generate._
val a = A(1)
val b = B("yep", flag = true)
println(a.generate) // prints 1: Int
println(b.generate) // prints "yep": String
}
However, when I try to generate a value for type L
, which depends on Types
, then the implicit resolution fails.
My goal is to obtain the following outputs:
println(L(a, 3).generate) \\ prints List(1,1,1)
println(L(b, 2).generate) \\ prints List("yep", "yep")
println(L(L(a,3), 2).generate) \\ prints List(List(1,1,1), List(1,1,1))
You probably want to properly link the Aux
types together.
First, let's make L
generic:
final case class L[T <: Types](value: T, n: Int) extends Types
Then, let's properly define its Generator
instance:
implicit def lGenerate[T <: Types, O](implicit aux: Aux[T, O]): Aux[Types.L[T], List[O]] =
new Generate[Types.L[T]] {
override type Out = List[O]
override def generate(in: Types.L[T]): Out =
List.fill(in.n)(aux.generate(in.value))
}
You can see the code running here.