带有Shapeless的基于类型的Id生成器

时间:2016-04-17 21:19:49

标签: scala shapeless hlist

我想实现一个基于类型的id生成器,我可以这样调用:

val nextPersonId = idGen.id[Person]

我希望IdGen成为一种特质(如果可能的话):

trait IdGen[L <: HList] {
  def id[T] = {
    if (T is an element in the L type) return 0L + number of previous calls for this T
    else throw new RuntimeException("no such T in L")
  }
}

class MyDao extends IdGen[Person :: Booking :: Charges :: HNil] {
  //something needed here?
}

如何使用Shapeless实现这一目标?

我尝试过使用scala反射typeTag并迭代toString结果,但它很难看。

1 个答案:

答案 0 :(得分:0)

好的,这是基于字符串的版本,它可能有许多关于大量案例的错误,并且要求实现者以提供隐式TypeTag的形式生成样板:

import shapeless._

import scala.collection.mutable
import scala.reflect.runtime.universe._

trait IdGen[L <: HList] {
  implicit val ltag: TypeTag[L]
  private val idGens = mutable.Map(typeTag[L].toString.split("""::\[""").toList.tail.map(_.split(""",shapeless""")(0) -> 0L): _*)

  def id[T: TypeTag] = {
    val t = typeOf[T].toString
    val id = idGens(t)
    idGens(t) = id + 1L
    id
  }
}

简单测试类:

import java.util.NoSuchElementException

import org.junit.{Assert, Test}
import shapeless._

import reflect.runtime.universe._

class Person

class Booking

class Charge

class MyDao(implicit val ltag: TypeTag[Person :: Booking :: HNil]) extends IdGen[Person :: Booking :: HNil]

class Testy {
  val myDao = new MyDao

  @Test(expected = classOf[NoSuchElementException])
  def test {
    Assert.assertEquals(0L, myDao.id[Person])
    Assert.assertEquals(1L, myDao.id[Person])
    Assert.assertEquals(0L, myDao.id[Booking])
    myDao.id[Charge]
  }
}

看到更好的实施将是非常有趣的。