Вычислить несколько общих типов в абстрактном классе, который должен быть доступен компилятору

Я работаю над абстрактным CRUD-DAO для своего проекта play2/slick2. Чтобы иметь удобные первичные идентификаторы типа безопасного типа, я использую Unicorn как дополнительную абстракцию и удобство поверх сликов MappedTo и ColumnBaseType.

Unicorn предоставляет базовый класс CRUD-DAO BaseIdRepository, который я хочу расширить для конкретных проектов. Подпись класса

class BaseIdRepository[I <: BaseId, A <: WithId[I], T <: IdTable[I, A]]
  (tableName: String, val query: TableQuery[T])
  (implicit val mapping: BaseColumnType[I])
  extends BaseIdQueries[I, A, T]

Это приводит к реализации DAO, которые выглядят примерно как

class UserDao extends 
  BaseIdRepository[UserId, User, Users]("USERS", TableQuery[Users])

Это кажется ужасно лишним для меня. Я смог предоставить tableName и query из T, предоставив мне следующую подпись на моем собственном Аннотация DAO

abstract class AbstractIdDao[I <: BaseId, A <: WithId[I], T <: IdTable[I, A]] 
  extends BaseIdRepository[I,A,T](TableQuery[T].baseTableRow.tableName, TableQuery[T])

Можно ли в Scala каким-то образом вывести типы I и A, чтобы сделать подпись такой, как это возможно? (Users - класс, расширяющийся IdTable)

class UserDao extends AbstractIdDao[Users]

Возможно ли это без отражения во время выполнения? Если только из-за отражения времени выполнения: как я могу использовать манифест в определении класса и насколько большой эффект влияет на реактивное приложение?

Кроме того, поскольку я довольно новичок в языке и сам работаю: это хорошая практика в Scala вообще?

Спасибо за помощь. Не стесняйтесь критиковать мой вопрос и английский. Разумеется, улучшения будут представлены в Unicorn git -repo

EDIT: Фактически, TableQuery[T].baseTableRow.tableName, TableQuery[T] не работает из-за требуемого типа класса ошибки, но T найден, IDEA был поверхностно тонким с ним, scalac не был.

Ответ 1

Что касается вашего первого вопроса, я столкнулся с этим при работе с Slick. Но если вы подумаете об этом, вы увидите, что не можете сделать это во время компиляции. Это связано с тем, что эта информация типа необходима для определения отношений между вашими параметрами типа. Если вы этого не сделаете, вы сможете построить классы BaseIdRepository, где типы не имеют смысла, например IdTables, где таблица не представляет проекцию. Поскольку вам нужны имена для каждого из этих отношений, вам нужны 3 именованных типа. Если вы опустите первый, можно создать IdRepository без проекции, содержащей Id; если вы опустите второй, можно иметь таблицу без столбца идентификатора; и если вы опустите третий, можно запросить таблицы, которые не имеют такой комбинации таблицы и проекции с идентификатором. Возможно, у вас не будут типы, определенные в вашем приложении, которые в настоящее время нарушат любые из этих правил, но компилятор этого не знает. Предоставление правильной информации о типе неизбежно.

Что касается вашего второго вопроса, очень нецелесообразно использовать отражение только потому, что вы считаете, что синтаксис является подробным. Если вы можете сделать гарантии о типах безопасности, просто указав параметры типа, я бы посоветовал вам это сделать. Это очень плохой вкус и стиль, чтобы написать Scala таким образом. Было бы иронично использовать typafe ID с Unicorn, а затем взломать его тип безопасности с отражением.

Кроме того, Manifest не то, что вы хотите: манифест не позволяет вам предоставлять меньше информации о типе компилятору, но только позволяет вам более гибко указывать, где вы это делаете. Это позволяет вам использовать знания компилятора типов во время компиляции, чтобы обойти некоторые проблемы, которые вводит стирание типа. Проблема, с которой вы сталкиваетесь здесь, не имеет ничего общего с стиранием типа, поэтому Manifest не будет работать. Наконец, отражение во время выполнения не поможет вам здесь, потому что внутренние функции Slick не позволят вам скомпилировать, если вы еще не предоставили информацию о типе.

Так что да, то, чего ты хочешь, невозможно. Scala (и Slick) нуждаются в полной информации во время компиляции, и никакой трюк не будет эффективен в обходе этого.