ClassTag and path-dependent types in a pie-like flavor

I am working on a small project and I am trying to make my database layer easily swappable between different profiles in order to write tests on an in-memory database. This question is inspired by this problem, but it has nothing to do with taking a picture.

I don't have much experience with dependent types, in my case I have the following trait which I use to abstract some types from the database:

trait Types {
  type A <: SomeType
  type B <: SomeOtherType
  val bTag: ClassTag[B]
}

      

Then I have another trait, which is basically a snippet of my (artificial) cake template:

trait BaseComponent {
  type ComponentTypes <: Types

  val a: Types#A
  implicit val bTag: ClassTag[Types#B]
}

      

Then I have the actual implementation of my component, which can be seen like this:

trait DefaultTypes {
  type A = SomeConcreteType
  type B = SomeOtherConcreteType
  val bTag = implicitly[ClassTag[B]]
}

trait DefaultBaseComponent extends BaseComponent {
  type ComponentTypes = DefaultTypes
  val ct = new ComponentTypes {}

  implicit val bTag = ct.bTag
}

      

I need a tag because later it will need a service (in my actual implementation, I use this type to abstractly express for different types of exceptions thrown by different DB libraries); I'm pretty sure there is a much better way to do what I am trying to do.

If I don't instantiate ComponentTypes

to get the tag, and I move the implicit collaboration code to the DefaultBaseComponent, it calls null

instead ClassTag

. I need to be able to reference the actual types that I am using (different A

and B

that I have in different environments), and I need to do this in other components without knowing what actual types they are.

My solution works, compiles and passes all the tests I wrote for it, can anyone help me improve it?

Thank!

+3


source to share


1 answer


Your example is a little unclear with all of these Default

and Component

- maybe a more specific example (like DatabaseService / MysqlDatabaseService) will become clearer?

You need to pass ClassTag

around wherever it is abstract - you can only "call" it when you have a concrete type. You might want to structure the concept of a value and its tag:

trait TaggedValue[A] {val a: A; val ct: ClassTag[A]}
object TaggedValue {
  def apply[A: ClassTag](a1: A) =
    new TaggedValue[A] {
      val a = a1
      val ct = implicitly[ClassTag[A]]
    }
}

      



but it's just a handy thing. You can also turn some of yours trait

into abstract class

es so you can use them [A: ClassTag]

to pass tags implicitly, but obviously this affects which classes you can inherit.

If you click null

it sounds like an issue with the traits initialization order, although without much error message it's tricky. You may be able to fix this problem by replacing some of yours val

with def

s or by using early initializers.

0


source







All Articles