ClassTag и зависимые от пути типы в аромате, похожем на образец торта
Я работаю над гладким проектом и пытаюсь сделать так, чтобы слой моей базы данных легко переключался между различными профилями, чтобы писать тесты в базе данных в памяти. Этот вопрос вдохновлен этой проблемой, но не имеет никакого отношения к самому пятну.
У меня нет большого опыта работы с зависимыми типами, в моем случае у меня есть следующая черта, которую я использую для абстрагирования некоторых типов от базы данных:
trait Types {
type A <: SomeType
type B <: SomeOtherType
val bTag: ClassTag[B]
}
Тогда у меня есть еще одна черта, которая в основном является частью моей (искусственной) модели торта:
trait BaseComponent {
type ComponentTypes <: Types
val a: Types#A
implicit val bTag: ClassTag[Types#B]
}
Затем у меня есть фактическая реализация моего компонента, которую можно увидеть следующим образом:
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
}
Мне нужен тег, потому что позже сервис будет нуждаться в нем (в моей реальной реализации я использую этот тип для абстрагирования от различных типов исключений, создаваемых различными библиотеками БД); Я совершенно уверен, что есть гораздо лучший способ сделать то, что я пытаюсь сделать.
Если я не буду создавать экземпляр ComponentTypes
trait для того, чтобы получить тег, и я перемещаю неявно вызывающий код в DefaultBaseComponent, он вызовет null
вместо ClassTag
, Мне нужно иметь способ ссылаться на фактические типы, которые я использую (разные A
а также B
что я имею в моих различных средах), и мне нужно сделать это в других компонентах, не зная, какие именно типы они.
Мое решение работает, компилирует и проходит все тесты, которые я для него написал, может кто-нибудь помочь мне в его улучшении?
Спасибо!
1 ответ
Ваш пример немного неясен со всеми этими Default
с и Component
s - может быть, более конкретный пример (например, DatabaseService / MysqlDatabaseService) сделает это более понятным?
Вам нужно пройти ClassTag
где бы он ни был абстрактным - вы можете вызвать его только тогда, когда у вас есть конкретный тип. Вы можете упаковать понятие значения и его тега:
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]]
}
}
но это просто удобство. Вы также можете включить некоторые из ваших trait
с в abstract class
ES, позволяя использовать [A: ClassTag]
передавать теги неявно, но, очевидно, это влияет на то, какие классы вы можете умножать наследовать.
Если ты бьешь null
Это похоже на проблему порядка инициализации черты, но без более конкретного сообщения об ошибке трудно помочь. Вы могли бы решить эту проблему, заменив некоторые из ваших val
с def
с или с помощью ранних инициализаторов.