繁体   English   中英

Scala - 创建类型集合的惯用方式

[英]Scala - Idiomatic way of creating a collection of types

问题

假设我有一个看起来像这样的 ADT

sealed trait TT
case class A(...) extends TT
case class B(...) extends TT
case class C(...) extends TT
// ... lot of others

我有这个函数,它为TT的子集返回 true 。 假设AC

def shouldIPublish(tt: TT): Boolean = ???

约束

  • 我们收到的值tt来自一个我无法真正改变的SortedSet 下面是函数shouldIPublish的调用方式: tts.exists(shouldIPublish)tts是一个SortedSet[TT] )。 它被擦除。
  • 我希望我的代码是可测试的。 对于我目前的解决方案,这是不可能的。 (见下文)
  • 代码应该针对每种TT类型进行编译。 如果tt的实际类型在子集(即AC )中,则返回true否则返回false

我不完美的解决方案

val shouldIPublishPF: PartialFunction[TT, Unit] = {
    case _: A =>
    case _: C =>
} 
def shouldIPublish(tt: TT): Boolean = shouldIPublishPF.isDefinedAt(tt)

我在这里使用部分函数是因为实际问题有点复杂。 我使用orElse将几个部分函数组合在一起。

这个解决方案很容易推理,也很简单。 但,

  • 这似乎不是很惯用。 我的意思是这个工具(部分函数)似乎不适合这项工作(测试类型是否包含在一组类型中)。
  • 我无法获得为部分函数定义的每个值 在这种情况下,它将是AC 当向部分函数添加新类型时,我的单元测试应该会失败。

替代方案(我能想到)

类集

也许是最简单的解决方案。 我可以有一个Set并使用.getClass获取类。 虽然看起来很丑。

Set(A.getClass, C.getClass)

无形的副产品

起初看起来很有希望。 非常地道。

type ShoudIPublish = A :+: C :+: CNil

不幸的是,我不知道如何在ShoudIPublish测试“包含” ShoudIPublish 而且我不知道我们是否可以收集所有类型的列表(这里是AC )。


你有什么建议吗?

类型类看起来像一个解决方案(如果你知道tt的类型,即在编译时TT特定子类型)

trait ShouldIPublish[T <: TT]
object ShouldIPublish {
  implicit val a: ShouldIPublish[A] = null
  implicit val c: ShouldIPublish[C] = null
}

def shouldIPublish[T <: TT : ShouldIPublish](tt: T) = ???

shouldIPublish(A())
//  shouldIPublish(B()) // doesn't compile
shouldIPublish(C())

标准类型类是shapeless.ops.coproduct.Inject

type ShoudIPublish = A :+: C :+: CNil

def shouldIPublish[T <: TT : Inject[ShoudIPublish, *]](tt: T) = ???

shouldIPublish(A())
//  shouldIPublish(B()) // doesn't compile
shouldIPublish(C())

OOP 风格的解决方案是使用特征(如果您可以修改层次结构)

trait ShouldIPublish

sealed trait TT
case class A() extends TT with ShouldIPublish
case class B() extends TT
case class C() extends TT with ShouldIPublish 

def shouldIPublish[T <: TT with ShouldIPublish](tt: T) = ???

shouldIPublish(A())
//  shouldIPublish(B()) // doesn't compile
shouldIPublish(C())

当您在编译时不知道TT特定子类型时,模式匹配解决方案可以在运行时工作

def shouldIPublish(tt: TT) = tt match {
  case _: A => println("A")
  case _: C => println("C")
}

shouldIPublish(A(): TT)// A
shouldIPublish(B(): TT)// MatchError
shouldIPublish(C(): TT)// C

或者

trait ShouldIPublish

sealed trait TT
case class A() extends TT with ShouldIPublish
case class B() extends TT
case class C() extends TT with ShouldIPublish 

def shouldIPublish(tt: TT) = tt match {
  case _: ShouldIPublish => println("A or C")
}

shouldIPublish(A(): TT)// A or C
shouldIPublish(B(): TT)// MatchError
shouldIPublish(C(): TT)// A or C

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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