如何在密钥类型和相应的值类型之间建模Scala中的关系

时间:2018-04-03 17:34:42

标签: scala type-systems

我想模拟每小时或每天的日志文件

sealed trait Level
case object Hour extends Level
case object Day extends Level

sealed trait Log[L <: Level]

我想要一个返回给定级别的所有日志的方法。所以这是签名

def byLevel[L <: Level](l: L) : Seq[Log[L]]

给出一些具体的日志实例(实际代码中还有很多):

case object HourlyLog extends Log[Hour.type]
case object DailyLog extends Log[Day.type]

我已经找到了以下实现:

object Log {
  case class Pair[L <: Level](level : L, logs: Seq[Log[L]])
  val logs = Seq(
    Pair(Hour, Seq(HourlyLog)),
    Pair(Day, Seq(DailyLog))
  )

  def byLevel[L <: Level](l: L) : Seq[Log[L]] = logs.find(_.level == l).get.logs.asInstanceOf[Seq[Log[L]]]
}

我的问题是:

  • 我们可以摆脱.asInstanceOf吗?
  • 我们可以摆脱Pair包装吗?
  • 有没有更好的方法来解决这个问题,而不使用无形(我知道HMap可以做到这一点)?

2 个答案:

答案 0 :(得分:1)

您似乎无论如何都要在随播对象上使用简单的对象比较,那么为什么要将它们与类型复杂化而不是将HourDay视为一个好的旧枚举?如果您想将Seq[DailyLog]Seq[HourlyLog]存储在同一个列表中,则需要asInstanceOf喜欢与否。

无论如何,您可以采取以下措施来摆脱PairasInstanceOf

  1. 完全忘记这些类型,将HourDay视为枚举值(仅限值,无类型)
  2. 使用implicits将类型直接映射到右侧列表(仅限类型,无值)
  3. 使用两种方法的奇怪组合(最接近你的代码,我不知道为什么你可能会想要这个),
  4. 这三种方法都不使用Pair,也不使用asInstanceOf(一个使用Map,但是......这就是你的Pair正在做的事情。代码的三面墙中的每一面都可以自行编译。

    只有值,没有类型,基本上是枚举

    sealed trait Level
    case object Hour extends Level
    case object Day extends Level
    
    sealed trait Log
    case object HourlyLog extends Log
    case object DailyLog extends Log
    
    object Log {
      val logs = Map[Level, Seq[Log]](
        Hour -> Seq(HourlyLog, HourlyLog, HourlyLog, HourlyLog),
        Day -> Seq(DailyLog)
      )
    
      def byLevel(l: Level): Seq[Log] = logs(l)
    }
    
    import Log._
    
    println(byLevel(Hour))
    println(byLevel(Day))
    

    输出:

    List(HourlyLog, HourlyLog, HourlyLog, HourlyLog)
    List(DailyLog)
    

    只有类型,没有对象值,暗示

    sealed trait Level
    sealed trait Hour extends Level
    sealed trait Day extends Level
    
    abstract class Log[L <: Level]
    
    case object HourlyLog extends Log[Hour]
    case object DailyLog extends Log[Day]
    
    object Log {
      case class Logs[L <: Level](val logs: Seq[Log[L]])
      implicit val hourlyLogs = Logs[Hour](Seq(
        HourlyLog, HourlyLog, HourlyLog, HourlyLog
      ))
      implicit val dailyLogs = Logs[Day](Seq(
        DailyLog
      ))
    
      def byLevel[L <: Level](implicit logs: Logs[L]): Seq[Log[L]] =
        logs.logs
    }
    
    import Log._
    println(byLevel[Hour])
    println(byLevel[Day])
    

    输出:

    List(HourlyLog, HourlyLog, HourlyLog, HourlyLog)
    List(DailyLog)
    

    混合方式,所有内容混合在一起

    sealed trait Level
    case object Hour extends Level
    case object Day extends Level
    
    sealed trait Log[L <: Level]
    
    case object HourlyLog extends Log[Hour.type]
    case object DailyLog extends Log[Day.type]
    
    object Log {
      case class Logs[L <: Level](val logs: Seq[Log[L]])
      implicit val hourlyLogs = Logs[Hour.type](Seq(
        HourlyLog, HourlyLog, HourlyLog, HourlyLog
      ))
      implicit val dailyLogs = Logs[Day.type](Seq(
        DailyLog
      ))
    
      def byLevel[L <: Level](l: L)(implicit logs: Logs[L]): Seq[Log[L]] =
        logs.logs
    
    }
    
    import Log._
    println(byLevel(Hour))
    println(byLevel(Day))
    

    输出:

    List(HourlyLog, HourlyLog, HourlyLog, HourlyLog)
    List(DailyLog)
    

答案 1 :(得分:0)

根据具体情况,有不同的摆脱铸造的方法。

  1. 如果您希望将Log[Hour]视为Log[Level]的特殊情况(因此,每次您需要Log [Level]时,您可以使用Log[Hour]替换它)使用协方差:{ {1}}

    您需要它才能将[+L <: Level]视为HourlyLogLog)的子类型

  2. 当您遇到可能混合的子类型集合的情况时,您可以使用collect:

    sealed trait Log[+L <: Level]
  3. 嗯,你仍然需要注释日志的类型,但它更干净,并且不要求你使用无形而不是任何其他魔法。

    至于摆脱Pair包装器 - 在JVM上你会有一个类型擦除。您需要与运行时检查的内容进行比较。我可以想到像def byLevel[L <: Level](l: L): Seq[Log[L]] = logs.collect { case Pair(l2, logs: Seq[Log[L]]) if l2 == l => logs }.flatten ClassTag这样的东西......但你仍然需要将它存储在某个地方(例如作为隐式构造函数参数TypeTag),然后与之进行比较。所以我确定它是,但它会产生一些样板。

    或者您可以使用sealed trait Log[+L <: Level: ClassTag]进行过滤/收集,但是您必须在某个地方明确地存储它以将HourlyLog映射到Hour过滤...我不确定值得努力。可能如果你在常规基础上进行,那么推出专用类型类并提供暗示是值得的......但这是你的号召。根据您的使用情况,可能是过度工程和纯度为了纯度...或保持代码库可维护的东西。在我谦卑的经历中,有时候&#34;脏&#34;解决方案做得很好。