Скала неявная в определении типа функции
У меня есть следующий абстрактный класс:
abstract class FieldProvider[+T: Writes](db: DB)(implicit i: RequestAction, j: ExecutionContext) {}
и следующие реализации:
class LengthProvider extends FieldProvider ...
object LengthProvider extends ((DB) => LengthProvider) {
def apply(v1: DB): LengthProvider = new LengthProvider(v1)
}
class WidthProvider extends FieldProvider ...
object WidthProvider extends ((DB) => WidthProvider) {
def apply(v1: DB): WidthProvider = new WidthProvider(v1)
}
Причина, почему у меня есть эти apply
методы, потому что мне нужна следующая карта конфигурации:
val providers: Map[String, ((DB) => FieldProvider)] = Map(
"length" -> LengthProvider,
"width" -> WidthProvider
)
Так что я могу инициализировать провайдеров строкой, содержащей их имя:
providers("length")(db) // returns a new instance of LengthProvider
Теперь моя проблема в том, что все эти конструкторы провайдеров требуют двух неявных переменных. Но я не знаю, как включить его в определение функции (DB) => FieldProvider
, Так что, по сути, apply
подпись метода должна быть что-то вроде (DB)(implicit RequestAction, ExecutionContext) => FieldProvider
, но я не знаю, есть ли правильный синтаксис для того, что я пытаюсь сделать.
Я мог бы также сдаться и передать их явно:
object WidthProvider extends ((DB, RequestAction, ExecutionContext) => WidthProvider) {
def apply(v1: DB, v2: RequestAction, v3: ExecutionContext): WidthProvider = new WidthProvider(v1)(v2,v3)
}
Но тогда мне придется передать их явно в другом месте, а не providers("length")(db)
Я должен был бы написать providers("length")(db, implicitly[RequestAction], implicitly[ExecutionContext])
, который не чувствует себя хорошо.
2 ответа
Давайте предположим FieldProvider
имеет 2 метода, которые нуждаются в последствиях. Более удобный способ избежать дублирования - передать их как неявные аргументы уровня конструктора, а затем все внутренние методы в FieldProvider
могу "поделиться" ими.
Тем не менее, это не поможет в вашем текущем дереве классов, поэтому исправить это вместо того, чтобы делать:
abstract class FieldProvider()(implicit param1: X1..) {
def test: T = param1.doSomething
}
Просто переместите неявное на уровень метода так, чтобы вы могли предоставлять его во время, отличное от расширения конструктора.
abstract class FieldProvider() {
def test()(implicit param1: X1): T = param1.doSomething
}
Так что, по сути,
apply
подпись метода должна быть что-то вроде(DB)(implicit RequestAction, ExecutionContext) => FieldProvider
, но я не знаю, есть ли правильный синтаксис для того, что я пытаюсь сделать.
Обратите внимание, что вы можете напрямую получить (в будущей версии Scala 2017) неявный тип функции.
См. Запрос на выдачу 1775 года самим Одерским.
Давайте помассируем определение
f1
немного, перемещая последний раздел параметров справа от знака равенства:
def f1(x: Int) = { implicit thisTransaction: Transaction =>
thisTransaction.println(s"first step: $x")
f2(x + 1)
}
Правая часть этой новой версии f1 теперь является неявным значением функции.
Какой тип этого значения?
Раньше это былоTransaction => Int
то есть знание о том, что функция имеет неявный параметр, потеряно в типе.Основным расширением, реализованным в запросе pull, является введение неявных типов функций, которые отражают значения неявных функций, которые у нас уже есть.
Конкретно, новый тип f1:
implicit Transaction => Int
Так же, как обычный синтаксис типа функции
A => B
десугары кscala.Function1[A, B]
, синтаксис неявного типа функцииimplicit A => B
Desugars дляscala.ImplicitFunction1[A, B]
,
То же самое верно и для других функций. С объединенным запросом тяги #1758 от dotty верхний предел для таких функций больше не равен 22.