Why do I get an Any when I expect a concrete type from a type parameter?

I have a description of a column of data of a given type. I have the following definitions:

object data {

  trait OfType[T]
  case object IntT extends OfType[Int]
  case object DoubleT extends OfType[Double]
  case object FloatT extends OfType[Float]

  type DSeq[X] = scala.collection.immutable.AbstractSeq[X]

  case class ColumnName[T](n:String, t: OfType[T])
  case class Column[T,F[_]<:DSeq[_]](n:F[T], of: ColumnName[T])
}

I would like to have functions that operate on these columns. For example select the minimum. I have tried the following:


  def min[T,F[_]<:data.DSeq[_]](col: data.Column[T,F])(using o:Ordering[T]): T = {
    col match {
      case data.Column(n,data.ColumnName(_,data.IntT)) => n.min(o)
      case data.Column(n,data.ColumnName(_,data.DoubleT)) => ???
      case data.Column(n,data.ColumnName(_,data.FloatT)) => ???

    }
  }

but this fails with:

error] 26 |      case data.Column(n,data.ColumnName(_,data.IntT)) => n.min(o)
[error]    |                                                                ^
[error]    |            Found:    (o : Ordering[T])
[error]    |            Required: Ordering[B]
[error]    |
[error]    |            where:    B is a type variable which is an alias of Any
[error]    |                      T is a type in method min which is an alias of Int

I am using dotty but I don’t think the error is specific to dotty. I have also tried several other variants with no success - same errors (see below). What am I doing wrong?

TIA


  def min2[T,F[_]<:data.DSeq[_]](col: data.Column[T,F])(using Ordering[T]): T = {
    col match {
      case data.Column(n,data.ColumnName(_,data.IntT)) => n.min(Ordering[Int])
      case data.Column(n,data.ColumnName(_,data.DoubleT)) => ???
      case data.Column(n,data.ColumnName(_,data.FloatT)) => ???

    }
  }

  def min3[T,F[_]<:data.DSeq[_]](col: data.Column[T,F])(using Ordering[T]): T = {
    col match {
      case c:data.Column[Int,_] => c.n.min(Ordering[Int])
      case _:data.Column[Double,_] => ???
      case _:data.Column[Float,_] => ???

    }
  }

  def min4[T,F[_]<:data.DSeq[_]](col: data.Column[T,F])(using Ordering[T]): T = {
    col match {
      case c:data.Column[Int,_] => c.n.min(Ordering[T])
      case _:data.Column[Double,_] => ???
      case _:data.Column[Float,_] => ???

    }
  }

  def min5[T,F[_]<:data.DSeq[_]](col: data.Column[T,F])(using Ordering[T]): T = {
    col match {
      case c:data.Column[Int,F] => c.n.min(Ordering[T])
      case _:data.Column[Double,_] => ???
      case _:data.Column[Float,_] => ???

    }
  }

It works if you tell the compiler that the holes in the higher-kinded parameters are filled with the same type.

def min[T,F[x]<:data.DSeq[x]]

I can see how otherwise F[T] could be considered a subtype of data.DSeq[Any], but it does feel like during the pattern match the type should be inferred to T instead of Any since you match against the objects with a known OfType[T], and I’m not sure why it that knot isn’t tied. Possibly it’s an artifact of the general left-to-right nature of the inference, where n is determined to be F[Any] before the compiler sees the IntT that would allow it to determine F[T] correctly. Perhaps it’s because min is a method of DSeq[t] and that since you only know F[T] <: DSeq[Any] then t = Any.

Thanks. That solves the problem for the examples that do instance matching (un-tupling). Strangly enough for the last variations the dotty compiler throws an exception. Going to try some more variations.

Regardless of compile errors, I would not expect 3 through 5 to work at runtime because of type erasure. The information about the type parameters is lost and it cannot be determined whether T = Int or not, so any Column you pass in will match the first case.

You are correct but it should a least compile with a waning. Also, if I use

def min[T,F[_]<:data.DSeq[T]]

it also seems to work.