简体   繁体   中英

How to constrain argument by type member of type parameter in scala?

The function reduceTo gets a type parameter indicating the kind of time series that should be returned, but the function splitInto requires a Period value. I want to make sure they are coherent so you can't do this:

reduceTo[MonthlySeries](period=Weekly)  // not ok
reduceTo[MonthlySeries](period=Monthly) // but this is ok
reduceTo[MonthlySeries]  // even better, derive the period from the type param

The type parameter should be sufficient but I can't seem to get the Period information I need from the parametrised type. Here's the simplified code:

sealed trait Period
case class Daily extends Period
case class Monthly extends Period


sealed abstract class TimeSeries {
    type T
}
case class DailySeries extends TimeSeries {
    type T = Daily.type
}
case class MonthlySeries extends TimeSeries {
    type T = Monthly.type
}

implicit class DailySeriesOps(lhs: DailySeries) {

    def reduceTo[G: TimeSeries](period: Period):G {
        // instead of sending the period in, how do I get
        // the period value from the type G?
        // Or at least constrain the period value?
        val s = splitInto(period, lhs)
        val reduced : G = ...
        // this is ugly
        assert(reduced.period == period, s"Reducing to $period but timeseries is ${reduced.period}")
    }
}

Initially I thought path dependent types would help so something like

def reduceTo[G: TimeSeries](period: G.T)

but that would require, from what I understand, that Daily would be a class defined inside DailySeries, which since it's used in other contexts is not ideal.

Another idea, which doesn't work, would be not to send the period parameter in but get the value from G, but since its instance doesn't exist yet, it doesn't work

def reduceTo... = {
  val period = G.T
  ...
}

[Update]

Also tried

def reduceTo[G : TimeSeries](period: TimeSeries#T: G = {
    val ts = reduce[F](period.asInstanceOf[GroupedPeriod], lhs)

But then this fails to compile with a type mismatch on reduceTo calls:

type mismatch;
[error]  found   : com.example.metrics.Periods.Monthly.type
[error]  required: com.example.metrics.calculations.TimeSeries#T

Any ideas?

According to your comment if GroupedSeries has type T field you could use period: G#T , or if don't you would need to add additional generic parameter and use something like:

  def reduceTo[G <: GroupedSeries[_], T <: TimeSeries](period: T#T): G = {
    //Your code
  }

And with this my compiler is saying:

DailySeriesOps().reduceTo[DailySeries,DailySeries](Monthly) // I don't compile that
DailySeriesOps().reduceTo[DailySeries,DailySeries](Daily) // that i will
DailySeriesOps().reduceTo[MonthlySeries,MonthlySeries](Monthly) // that i will too
DailySeriesOps().reduceTo[MonthlySeries,MonthlySeries](Daily) // and not this one

But your question is not easy reproducible, so I don't know if this can answer your question, I hope it helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM