Reputation: 8281
By design, we know for sure that we have an instance of HourlyDateFormat
How to avoid calling asInstanceOf in this case (i.e how to help the compiler to infer the type) ?
sealed trait StorageLayout extends Product with Serializable
case object Hourly extends StorageLayout
case object Daily extends StorageLayout
sealed trait DateFormat[S <: StorageLayout]
sealed abstract class HourlyDateFormat extends DateFormat[Hourly.type] {
def format(localDate: LocalDate): String = ???
def format(localDateTime: LocalDateTime): String = ???
}
sealed abstract class DailyDateFormat extends DateFormat[Daily.type] {
def format(localDate: LocalDate): String = ???
}
class Log[S <: StorageLayout](storageLayout: S, dateFormat: DateFormat[S]) {
def getPath(date: LocalDate): String =
dateFormat match {
case hdf: HourlyDateFormat => hdf.format(date)
case ddf: DailyDateFormat => ddf.format(date)
}
@SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))
def getPath(date: LocalDateTime)(implicit ev: S =:= Hourly.type): String = {
assert(ev == ev)
dateFormat.asInstanceOf[HourlyDateFormat].format(date)
}
}
Upvotes: 3
Views: 263
Reputation: 7604
A quick fix would be something like this:
class Log[S <: StorageLayout, D <: DateFormat[S]](storageLayout: S, dateFormat: D) {
def getPath(date: LocalDateTime)(implicit ev: D <:< HourlyDateFormat): String =
dateFormat.format(date)
}
However, I don't think you've designed this the right way. It'd probably be better to have a separate trait for each type of format. This makes it more scalable, since you don't need to add a case in your match expression for each different class, the right method gets selected automatically at runtime. You still have to use those evidence parameters, which I don't like, but it still feels cleaner to me.
Edit: I've updated the code so that everything extends FormatLocalDate
and you only need an evidence parameter for getPath(LocalDateTime)
sealed trait FormatLocalDate[S <: StorageLayout] {
def format(localDate: LocalDate): String
}
sealed trait FormatLocalDateTime[S <: StorageLayout] extends FormatLocalDate[S] {
def format(localDate: LocalDateTime): String
}
sealed abstract class HourlyDateFormat extends FormatLocalDateTime[Hourly.type] {
def format(localDate: LocalDate): String = ???
def format(localDateTime: LocalDateTime): String = ???
}
sealed abstract class DailyDateFormat extends FormatLocalDate[Daily.type] {
def format(localDate: LocalDate): String = ???
}
class Log[S <: StorageLayout, D <: FormatLocalDate[S]](storageLayout: S, dateFormat: D) {
def getPath(date: LocalDate): String =
dateFormat.format(date)
def getPath(date: LocalDateTime)(implicit ev: D <:< FormatLocalDateTime[S]): String =
dateFormat.format(date)
}
Upvotes: 3
Reputation: 51648
Try to add one more implicit parameter
def getPath(date: LocalDateTime)(implicit ev: S =:= Hourly.type, ev1: DateFormat[S] =:= HourlyDateFormat): String = {
//assert(ev == ev)
dateFormat.format(date)
}
Assertion looks strange: assert(ev == ev)
.
Or just
def getPath(date: LocalDateTime)(implicit ev1: DateFormat[S] =:= HourlyDateFormat): String
Fixed version (I added one more type parameter, it's now similar to the first @user's version)
class Log[S <: StorageLayout, D <: DateFormat[S]](storageLayout: S, dateFormat: D) {
def getPath(date: LocalDate): String =
dateFormat match {
case hdf: HourlyDateFormat => hdf.format(date)
case ddf: DailyDateFormat => ddf.format(date)
}
def getPath(date: LocalDateTime)(implicit
ev: S =:= Hourly.type,
ev1: D <:< HourlyDateFormat,
): String = {
dateFormat.format(date)
}
}
val log = new Log(Hourly, new HourlyDateFormat(){})
print(log.getPath(LocalDateTime.now()))
Upvotes: 5
Reputation: 27535
Generally things like that are kind-of type-classy, so I would do it this way instead:
trait DailyFormatter[S] {
def formatDate(localDate: LocalDate): String
}
trait HourlyFormatter[S] {
def formatDateTime(localDateTime: LocalDateTime): String
}
implicit val dailyFormats: DailyFormatter[Daily]
implicit val hourFormats: DailyFormatter[Hourly] with HourlyFormatter[Hourly]
class Log[S <: StorageLayout](storageLayout: S, dateFormat: DateFormat[S]) {
def getPath(date: LocalDate)(implicit formater: DailyFormatter[S]): String =
formater.formatDate(date)
def getPath(date: LocalDateTime)(implicit formater: HourlyFormatter[S]): String =
formater.formatDateTime(date)
}
It has the advantage that you don't have to be aware of existence of types HourlyDateFormat
and DailyDateFormat
to make it work.
Upvotes: 5