如何在Scala中实现Typesafe域存储库?

Igo*_*kov 2 generics scala dotty

我想实现通用和类型安全的域存储库。说我有

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?
}
Run Code Online (Sandbox Code Playgroud)

结果,我想要以下api:

domainRepo.putAll(1, 2, 3, "foo", "bar") //Should work
domainRepo.putAll(1, 2, true, "foo") // should not compile because of boolean value
Run Code Online (Sandbox Code Playgroud)

问题是如何实现这一目标?

因此,我只看到一种使其成为类型安全的方法。可以对任何类型的样式进行匹配

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
}
Run Code Online (Sandbox Code Playgroud)

但是如果我在这里有10000个类型该怎么办?真是一团糟!

对我来说,目前尚不清楚的另一种方法是使用dotty类型。(或)如下所示:

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
}
Run Code Online (Sandbox Code Playgroud)

所以你怎么看?可能吗?

我见过的简单方法是

Map[Class[_], Repo[_]]
Run Code Online (Sandbox Code Playgroud)

但是这种方式会导致很多错误

Dmy*_*tin 5

看来您正在寻找类型类别

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)
}
Run Code Online (Sandbox Code Playgroud)

如果要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
Run Code Online (Sandbox Code Playgroud)

  • 我只是将其添加为使用`HList`的替代方法。如果他一次放置一个元素,则可以为该元素解析typeclass。 (3认同)