В качестве приемлемого решения можно исключить возможность перемещения функции предварительной обработки с экземпляров на связанный объект, основная трудность заключается в том, что вы хотите иметь возможность абстрагироваться от арности и типов (т. Е. Формы) вашего случая класса. Это возможно с помощью значений реализации и значений полиморфной функции HList
от shapeless.
Первой некоторые предварительные,
import shapeless.HList._
import shapeless.Functions._
import shapeless.Poly._
import shapeless.TypeOperators._
// Implementation of your Input wrapper
case class Input[T](value: T)
// Value extractor as a shapeless polymorphic function value
object value extends (Input ~> Id) {
def default[T](i : Input[T]) = i.value
}
Теперь мы можем определить препроцессор базового класса, который обеспечивает способ применения, который принимает HList
из Input
типов, карты полиморфного функцию value
через него (то есть. Выполняет предварительную обработку) и затем передает результирующий HList
из не- Input
типов предоставленному случай, конструктор класса (который приведен в hlisted форме, смотри ниже),
// Pre-processer base class
abstract class Preprocessor[In <: HList, Out <: HList, R](ctor : Out => R)
(implicit mapper : MapperAux[value.type, In, Out]) {
def apply(in : In) = ctor(in map value)
}
Теперь определим класс случай с типами компонентов пост-обработки,
case class Foo(input1 : Int, input2 : String)
и добавить одну строку шаблонный,
object FooBuilder extends Preprocessor((Foo.apply _).hlisted)
(здесь метод объекта компаньон завод Foo предусмотрен как Preprocessor
аргумент конструктора в HListed форме, как требуется выше.)
Теперь мы можем построить экземпляры Foo
, используя FooBuilder
.
val foo = FooBuilder(Input(23) :: Input("foo") :: HNil)
К сожалению, это не так (в настоящее время) можно объединить FooBuilder
объект с объектом Foo
компаньона: если вы пытаетесь иметь Foo
компаньона продлить Preprocessor
вы обнаружите, что метод Foo
завода не доступен который должен быть передан как аргумент конструктора Preprocessor
.
Чтобы показать, что это решение действительно абстрагируясь над типом и арностью, вот как мы могли бы добавить второй по-разному формы класса случай,
case class Bar(input1 : Int, input2 : String, input3 : Boolean)
object BarBuilder extends Preprocessor((Bar.apply _).hlisted)
val bar = BarBuilder(Input(23) :: Input("foo") :: Input(true) :: HNil)
Учитывая значение типа Input [T] как получить соответствующее значение типа T? –
Я знаю способ, которым вы перехватываете конструктор из черты. Может быть, есть лучший подход к тому, что вы хотите. Я предполагаю, что вы захотите создать MagicTrait, потому что вы не хотите повторять этот код предварительной обработки в каждом классе. Какую предварительную обработку вы хотите сделать для этих аргументов конструктора? –
Хорошая параллель с моей ситуацией заключается в том, что каждый из аргументов является функцией, и я хочу запускать каждую функцию, а затем вызывать новую функцию с выходом каждой из функций. –