Wrestling with type mismatch: underlying type not equal to required type

Hello,

I get the error:

type mismatch;
[error]  found   : acc.type (with underlying type ColumnMajor.Metrics#AllColumns)
[error]  required: e.AllColumns
[error]            val nacc = e.append(acc, all)
[error]                                ^
[error] two errors found

When using the following test example:

object ColumnMajor {

  class Metric[T](val value:T)

  case class M1(override val value: Double) extends Metric[Double](value)
  case class M2(override val value: Double) extends Metric[Double](value)

  trait Metrics {
    type All
    type AllColumns

    val calculate: All
    val zero : AllColumns
    def append(acc: AllColumns, e: All) : AllColumns
  }

  case class Binary() extends Metrics {
    override type All = (M1,M2)
    override type AllColumns = (List[M1], List[M2])
    override val calculate : All = (M1(0),M2(1))
    override val zero: AllColumns = (List[M1](), List[M2]())

    override def append(acc: AllColumns, e: All): AllColumns = {
      (e._1 :: acc._1, e._2 :: acc._2)
    }
  }

  trait MetricEval {

    type Z = Metrics

    def collectOnly3(metrics: => Stream[Z]) = {
      val zero: Z#AllColumns = metrics.head.zero
      metrics.foldLeft(zero) {
         (acc, e: Z) =>
           val all: e.All = e.calculate
           val nacc = e.append(acc, all)
           nacc
       }
    }
  }
}

How can (should) I encode the fact that e.All in collectOnly3 is in fact Z#All and the same for the
case of AllColumns? What is the correct way of encoding such a usage pattern?

TIA.

Ok, realized this may be an issue with dependent types. Still looking for a way to solve this.

The problem is that for your Stream[Metrics] the compiler can’t prove that every element in that stream will have the same All and AllColumns types. Take this code for example:

case class Unary() extends Metrics {
  override type All = M1
  override type AllColumns = Array[M1]
  ...
}

def eval: MetricEval = ???
eval.collectOnly3( Stream(Unary(), Binary()) )

That would be valid code if your original code compiled. But it wouldn’t make any sense, because the methods of Unary and Binary work with totally different types.

One solution would be to use type parameters instead of members. Then you can (more easily) prove to the compiler that all metrics will have compatible types in def collectOnly3[A,B](metrics: => Stream[Metrics[A,B]]).

/1704[quote=“Jasper-M, post:3, topic:1713”]
the compiler can’t prove that every element in that stream will have the same All and AllColumns types.
[/quote]

Which should of been obvious. So If I use:

  trait Metrics[All,AllColumns] {
    type A = All
    type C = AllColumns

    val calculate: All
    val zero : AllColumns
    def append(acc: AllColumns, e: All) : AllColumns
  }

  case class Binary() extends Metrics[(M1,M2), (List[M1], List[M2])] {

    type All = (M1, M2)
    type AllColumns = (List[M1], List[M2])

    override val calculate: All = (M1(0),M2(1))
    override val zero: AllColumns = (List[M1](), List[M2]())

    override def append(acc: AllColumns, e: All): AllColumns = {
      (e._1 :: acc._1, e._2 :: acc._2)
    }
  }

  trait MetricEval {
    type Z[A,B] = Metrics[A,B]

    def collect[A,B](metrics: => Stream[Z[A,B]]) = {
      val zero: B = metrics.head.zero
      metrics.foldLeft(zero) {
        (acc, e: Z[A,B]) =>
          val all: A = e.calculate
          val nacc: B = e.append(acc, all)
          nacc
      }
    }
  }

it works perfectly. However I was fixated on using type members. In fact I had already tried the solution shown in:

https://users.scala-lang.org/t/why-do-i-need-to-indicate-the-type-of-functions-generic-type-parameter-that-should-be-inferred

And it failed for the same reason (with the same error). But your answer set me on the right track. Thanks. So here is an alternate solution using parameter refinement:

  trait Metrics {
    type All
    type AllColumns

    val calculate: All
    val zero : AllColumns
    def append(acc: AllColumns, e: All) : AllColumns
  }

  case class Binary() extends Metrics {

    type All = (M1, M2)
    type AllColumns = (List[M1], List[M2])

    override val calculate: All = (M1(0),M2(1))
    override val zero: AllColumns = (List[M1](), List[M2]())

    override def append(acc: AllColumns, e: All): AllColumns = {
      (e._1 :: acc._1, e._2 :: acc._2)
    }
  }

  trait MetricEval {

    def collect[A,B](metrics: => Stream[Metrics { type All = A ; type AllColumns = B }]) = {
      val zero: B = metrics.head.zero
      metrics.foldLeft(zero) {
        (acc, e ) =>
          val all:A = e.calculate
          val nacc:B = e.append(acc, all)
          nacc
      }
    }
  }

Once again thanks.