Hello,

I am attempting to express a set of “constraints” that must be processed into a “function” that uses those constraints to generate values. An example will make it easier to understand. Say I have these set of “constraints”:

```
sealed trait Param[T]
final case class Range[T](start: T, end: T, delta: T)(implicit num: Numeric[T]) extends Param[T]
final case class Options[T](e: Seq[T]) extends Param[T]
final case class Composite[T1,T2](paramA: Param[T1], paramB: Param[T2]) extends Param[(T1,T2)]
```

Now I can construct a search space so:

```
val p1: Range[Int] = Range(1, 10, 1)
val p2: Options[String] = Options(List("a", "b", "c"))
val p3: Composite[Int, String] = Composite(p1, p2)
```

The idea is then to have several strategies that can be “applied” to these constraints to convert them into a sampler (that generates values according to those constraints). For example I could have:

```
def gridSearch[T](p: Param[T]): Sample[T] = p match {
case g@Range(_, _, delta) =>
val r: Sample[T] = linear(g, delta)
r
case l@Options(_) =>
val r = list(l)
r
case Composite(paramA, paramB) =>
val a = gridSearch(paramA)
val b = gridSearch(paramB)
val c = cartesian(a, b)
c
}
```

I could then use this so:

```
val s1: Sample[Int] = gridSearch(p1)
val s2: Sample[String] = gridSearch(p2)
val s3: Sample[(Int, String)] = gridSearch(p3)
val s4: Sample[(Int, (Int, String))] = gridSearch(Composite(p1,p3))
```

Notice that the return type is a “compound tuple” that will then be used as a function parameter elsewhere.

All seems to be fine and dandy as long as the functions `linear`

, `list`

and `cartesian`

are also generic. However, my problem is that functions like `linear`

and `list`

are **not** (but `cartesian`

is).

More concretely: in the example above `p1`

is of type `Range[Int]`

and results in a `Sample[Int]`

but it could be of any other type. The following is a “complete” example showing the issue:

```
object Exp7c {
sealed trait Sampler[T]
case class IntUser(s:Int,e:Int) extends Sampler[Int]
case class DoubleUser(s:Double,e:Double) extends Sampler[Double]
sealed trait Param[T]
final case class Range[T](start: T, end: T, delta: T) extends Param[T]
final case class Options[T](e: Seq[T]) extends Param[T]
final case class Composite[T1,T2](paramA: Param[T1], paramB: Param[T2]) extends Param[(T1,T2)]
val p1: Range[Int] = Range(1, 10, 1)
val p2: Options[String] = Options(List("a", "b", "c"))
val p3: Composite[Int, String] = Composite(p1, p2)
def useInt(p : Range[Int]): Sampler[Int] = IntUser(p.start, p.end)
def useDouble(p : Range[Double]): Sampler[Double] = DoubleUser(p.start, p.end)
def process[T](p: Param[T]): Sampler[T] = p match {
case g@Range[Int](_, _, delta) =>
val r = useInt(g)
r
case g@Range[Double](_, _, delta) =>
val r = useDouble(g)
r
}
}
```

Of course `process`

does not compile. So my question is, short of creating each `Param[T]`

for a specific type, what is the best way to model this in Scala. Any pointers or examples will be greatly appreciated.