0

I want to implement a type based id generator that I can call like this:

val nextPersonId = idGen.id[Person]

I'd like IdGen to be a trait (if possible):

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?
}

How does one go about to implement this with Shapeless?

I've tried using scala reflection typeTag and iterate over the toString result, but it's ugly.

eirirlar
  • 814
  • 6
  • 24
  • I'm not exactly certain what you're trying to do here. Do you want the compiler to increment IDs for you? Do you want the compiler to simply enforce usage of a certain ID generator? The former is not going to be possible if you want IDs generated at runtime since the number of IDs generated might change based on control flow. The latter might not require Shapeless depending on what you're trying to do. – badcook Apr 18 '16 at 02:02
  • @badcook I'll write an answer using toString parsing of the typeTag of L myself, and it will hopefully become clearer :) – eirirlar Apr 18 '16 at 07:10

1 Answers1

0

Okay here's the string based version which probably has bugs regarding lots of corner cases, and which requires implementors to produce boilerplate in the form of supplying an implicit 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
  }
}

Simple test class:

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]
  }
}

It would be very interesting to see a better implementation.

eirirlar
  • 814
  • 6
  • 24