Частично примененный тип лямбда в Scala с добрым проектором

Рассмотрим следующее определение типа:

trait LiftF[F[_], G[_]] {
  def liftF[A](fa: F[A]): G[A]
}

При предоставлении требования для имплицита этого типа в границах контекста (используя плагин Kind Projector) мы должны написать это так:

def func[A, G[_], F[_]: LiftF[?[_], G]](a: F[A]): G[A]

Я хотел бы избавиться от части ?[_], поэтому моей первоначальной догадкой было написать тип To[G[_]], который возвращает LiftF[?[_], G], чтобы преобразовать приведенное выше определение функции в

def func[A, G[_], F[_]: LiftF.To[G]](a: F[A]): G[A]

Однако при написании определения типа To как

type To[G[_]] = LiftF[?[_], G]

Я получаю следующую ошибку компиляции:

Error:(17, 20) type Λ$ takes type parameters
type To[G[_]] = LiftF[?[_], G]

Попытка переписать его с помощью экзистенциальных типов приводит к следующему определению типа:

type To[G[_]] = LiftF[F, G] forSome { type F[X] }

Это прекрасно компилируется, но, что неудивительно, не может быть применено к другим параметрам типа, поэтому желаемое определение функции не может быть достигнуто.

Мне удалось реализовать часть «частичного приложения» с кодом, вдохновленным шаблоном aux:

trait To[G[_]] {
  type From[F[_]] = LiftF[F, G]
}

К сожалению, это оставляет меня с синтаксисом, который, возможно, хуже исходного:

def func[A, G[_], F[_]: LiftF.To[G]#From](a: F[A]): G[A]

Мой вопрос: могу ли я добиться изначально предложенного синтаксиса в Scala с помощью доброго проектора, или я должен просто придерживаться ?[_]?


person a7emenov    schedule 21.03.2019    source источник


Ответы (1)


Насколько я понимаю, вид-проектор тут особо не поможет:

type To[G[_]] = LiftF[?[_], G]

будет просто механически переписан во что-то вроде

type To[G[_]] = ({ type T[F[_]] = LiftF[F, G] })#T

но он недействителен в версии 2.12.x, так как в правой части определения ожидается простой тип типа *.

Если вы переместите параметр F в левую часть, вы получите

type To[G[_], F[_]] = LiftF[F, G]

который вы затем должны использовать как To[G, ?[_]], который, очевидно, тоже ничего вам не купит, он просто меняет порядок аргументов. Поэтому я предлагаю просто использовать LiftF[?[_], G] и утешаться тем фактом, что вам не нужно явно записывать ({ type L[F[_]] = LiftF[F, G] })#L.


Кстати, в Dotty это прекрасно работает:

trait LiftF[F[_], G[_]] {
  def liftF[A](fa: F[A]): G[A]
}

type To[G[_]] = [F[_]] => LiftF[F, G]
def f[A, G[_], F[_]: To[G]](a: F[A]): G[A] = implicitly[LiftF[F, G]].liftF(a)
person Andrey Tyukin    schedule 21.03.2019
comment
Красиво, я бы хотел, чтобы Дотти была признана больше, чем она есть. - person Markus Appel; 21.03.2019
comment
Спасибо за совет Доти. Наверное, это и будет повод наконец-то попробовать. - person a7emenov; 21.03.2019
comment
Синтаксис изменен с => на =>> с помощью Используйте =›› для лямбда-выражений типа #6558 - person Mario Galic; 05.04.2020