Я пытаюсь выполнить Естественное Преобразование между видами * -> * -> *
поэтому хочу взять F[A, B] => G[A, B]
В частности, я пытаюсь определить DSL, который затем можно преобразовать в фактические определения функций, поэтому MyDSL[A, B] => Function[A, B]
Вот определение естественной трансформации:
trait ~~>[F[_, _], G[_, _]] {
def apply[A, B](fab: F[A, B]): G[A, B]
}
object ~~> {
def apply[F[_, _], G[_, _]](implicit f2g: F ~~> G): F ~~> G = f2g
}
DSL выглядит так:
sealed trait MyDSL[A, B]
object MyDSL {
case object Add1 extends MyDSL[Int, Int]
case object Show extends MyDSL[Int, String]
implicit def dsltoF: MyDSL ~~> Function = new ~~>[MyDSL, Function] {
override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
case Add1 => i => i + 1
case Show => i => i.toString
}
}
}
Использование естественного преобразования напрямую работает нормально:
dsltoF(Add1)
вывод: res0: Function[Int,Int] = MyDSL$$anon$2$$Lambda$1816/700824958@6f3aa425
Это работает даже в случае, когда возвращаемой функцией является метод, принимающий 2 параметра типа.
Когда я пытаюсь определить объект DSL, который преобразуется с использованием универсального метода одного параметра типа, у него возникают проблемы.
case class Id[A]() extends MyDSL[A, A]
implicit def dsltoF: MyDSL ~~> Function = new ~~>[MyDSL, Function] {
override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
case Id() => identity[A] _
case Add1 => i => i + 1
case Show => i => i.toString
}
}
Я получаю found A required B
ошибку компиляции.
В этом случае Скала не распознает, что В - это А.
Я понимаю, почему параметры типа A & B не обязательно соотносятся должным образом с определением функции, которую я возвращаю, поэтому даже пишем:
case Add1 => i => i + 1
в IntelliJ есть красные линии, так как он не понимает, что, хотя Add "является" MyDSL [Int, Int]. Хотя Скала в порядке с этим.
Параметры типа открыты для всех возможностей в сигнатуре метода apply в Natural Transformation, но в этом случае требуется какое-то ограничение. Я предполагаю, что поскольку в классе дел DSL нет значения для ограничения параметра типа, он сводится к сопоставлению с шаблоном, которое уже прошло, когда Scala интерпретирует сигнатуру метода, и поэтому оно ожидает другой тип B и лает.
Я, конечно, могу обойти это через .asInstanceOffness, но я имею в виду, давай.
Буду признателен за любые мысли о другой стратегии, чтобы заставить это работать.