scala - 如何在 scala 中实现类型安全的域存储库?
问题描述
我想实现通用和类型安全的域存储库。说我有
trait Repo[Value] {
def put(value: Value): Unit
}
case class IntRepo extends Repo[Int] {
override def put(value: Int): Unit = ???
}
case class StringRepo extends Repo[String] {
override def put(value: String): Unit = ???
}
case class DomainRepo(intRepo: IntRepo, stringRepo: StringRepo) {
def putAll[?](values: ?*): Unit // what type should be here?
}
结果我想要以下api:
domainRepo.putAll(1, 2, 3, "foo", "bar") //Should work
domainRepo.putAll(1, 2, true, "foo") // should not compile because of boolean value
问题是如何实现这一目标?
所以,我只看到一种使其类型安全的方法。这是对任何类型进行模式匹配,例如
def putAll(values: Seq[Any]) => Unit = values.foreach {
case str: String => stringRepo.put(str)
case int: Int => intRepo.put(int)
case _ => throw RuntimeException // Ha-Ha
}
但是如果我在这里有 10000 种类型呢?那将是一团糟!
我现在还不清楚的另一种方法是使用 dotty type | (或)如下:
type T = Int | String | 10000 other types // wouldn't be a mess?
def putAll(t: T*)(implicit r1: Repo[Int], r2: Repo[String] ...) {
val myTargetRepo = implicitly[Repo[T]] // would not work
}
所以你怎么看?有可能吗?
我见过的最简单的方法是
Map[Class[_], Repo[_]]
但这种方式允许做很多错误
解决方案
看来您正在寻找类型类
trait Repo[Value] {
def put(value: Value): Unit
}
implicit val intRepo: Repo[Int] = new Repo[Int] {
override def put(value: Int): Unit = ???
}
implicit val stringRepo: Repo[String] = new Repo[String] {
override def put(value: String): Unit = ???
}
case object DomainRepo {
def putAll[Value](value: Value)(implicit repo: Repo[Value]): Unit = repo.put(value)
}
如果想domainRepo.putAll(1, 2, 3, "foo", "bar")
编译不想domainRepo.putAll(1, 2, true, "foo")
编译,可以尝试使用异构集合(HList
)。
import shapeless.{HList, HNil, ::, Poly1}
import shapeless.ops.hlist.Mapper
trait Repo[Value] {
def put(value: Value): Unit
}
implicit val intRepo: Repo[Int] = new Repo[Int] {
override def put(value: Int): Unit = ???
}
implicit val stringRepo: Repo[String] = new Repo[String] {
override def put(value: String): Unit = ???
}
case object DomainRepo {
def put[Value](value: Value)(implicit repo: Repo[Value]): Unit = repo.put(value)
object putPoly extends Poly1 {
implicit def cse[Value: Repo]: Case.Aux[Value, Unit] = at(put(_))
}
def putAll[Values <: HList](values: Values)(implicit
mapper: Mapper[putPoly.type, Values]): Unit = mapper(values)
}
DomainRepo.putAll(1 :: 2 :: 3 :: "foo" :: "bar" :: HNil)
// DomainRepo.putAll(1 :: 2 :: true :: "foo" :: HNil) // doesn't compile
推荐阅读
- web - Flutter for web 如何存储凭据?
- flutter - 事件的返回函数
- angular - `try_files` 不适用于 nginx docker 映像
- arrays - 元素大于其长度的最长子序列
- r - R元编程:返回填充了参数值的函数体
- ruby-on-rails - Ruby 和 Rails 升级后的“参数数量错误(给定 1,预期为 0)”
- google-apps-script - 如何将 Checkbox 和 Multiple Choice 的可选值放入 Google Apps 脚本的预填表单中?
- react-hooks - 功能组件中的多个效果失败,服务器状态代码为 500
- amazon-web-services - Sagemaker KMeans 内置 - 文件列表 csv 作为输入
- javascript - JavaScript 等价于 try.. 中的 "else".. 除了.. else?