добрый проектор возвращает странные результаты

У меня есть такие типы:SomeTypeClass
Тип с более высоким родством, который имеет один параметр типа вида * => * => *

trait SomeTypeClass[P[_, _]] {
    def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                              (pab: P[A, B])
                              (implicit ev: Strong[P],
                              ev2: Choice[P],
                              ev3: Applicative[F]): P[S, T]
}

Targetкоторый принимает три параметра
типа: конструктор типаF[_] и два полиморфных типа A, B

case class Target[F[_], A, B](f: A => F[B])

Я хочу реализовать экземпляр SomeTypeClass of Target.
Я использую плагин kind-Propector для создания частично нанесенного шрифта.
Моя желаемая подпись метода должна быть:

implicit def instance: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
  override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                      (pab: Target[F, A, B])
                                      (implicit ev: Strong[Target[F, *, *]], 
                                       ev2: Choice[Target[F, *, *]], 
                                       ev3: Applicative[F]): Target[F, S, T] = ???
}

Я пробовал использовать этот синтаксис, используя два параметра звезды:

 implicit def instance[F[_]]: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
    override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                     (pab: Target[F, A, B])
                                     (implicit ev: Strong[Target[F, *, *]],
                                      ev2: Choice[Target[F, *, *]],
                                      ev3: Applicative[F]): Target[F, S, T] = ???
}

Но F[_] объявленный на уровне экземпляра, затеняет F[_] объявлены в тестовом методе (я хочу, чтобы они были того же F), поэтому я перешел на синтаксис λ и получил два разных нежелательных результата.

Первый, использующий λ[(F, A, B) => Target[F, A, B]] создан для pab параметрpab: Target[A, B, B] вместо того pab: Target[F, A, B] а также для возвращаемого типа Target[S, T, B] вместо того Target[F, S, T]

Второй, использующий букву F в конце лямбда-параметров тройного типа (почему???)
λ[(A, B, F) => Target[F, A, B]] сгенерировал правильные типы для pab параметр и тип возвращаемого значения, но для каждого из неявных параметров тип Strong[λ[(A, B, F) => Target[F, A, B]]] вместо того Strong[Target[F, *, *]]]

Полный код:

  import cats.Applicative
  import cats.arrow.{Choice, Strong}

  final case class Target[F[_], A, B](f: A => F[B])

  trait SomeTypeClass[P[_, _]] {
    def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                              (pab: P[A, B])
                              (implicit ev: Strong[P],
                               ev2: Choice[P],
                               ev3: Applicative[F]): P[S, T]
  }

  object SomeTypeClass {
    implicit def instance1: SomeTypeClass[λ[(F, A, B) => Target[F, A, B]]] = new SomeTypeClass[λ[(F, A, B) => Target[F, A, B]]] {
      override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                         (pab: Target[A, B, B])
                                         (implicit ev: Strong[Target],
                                          ev2: Choice[Target],
                                          ev3: Applicative[F]): Target[S, T, B] = ???
    }

    implicit def instance2: SomeTypeClass[λ[(A, B, F) => Target[F, A, B]]] = new SomeTypeClass[λ[(A, B, F) => Target[F, A, B]]] {
      override def test[F[_], S, T, A, B](f: (A => F[B]) => S => F[T])
                                         (pab: Target[F, A, B])
                                         (implicit ev: Strong[λ[(A, B, F) => Target[F, A, B]]],
                                          ev2: Choice[λ[(A, B, F) => Target[F, A, B]]],
                                          ev3: Applicative[F]): Target[F, S, T] = ???
    }
  }

Могу ли я добиться желаемого синтаксиса с помощью этого плагина?
Почему плагин генерирует разные типы для разного порядка параметров лямбда-типа?

1 ответ

Решение

Если бы я понял

Но F[_] объявленный на уровне экземпляра затеняет F[_] объявлены в тестовом методе (я хочу, чтобы они были такими же F)

правильно, вам нужен ваш экземпляр для SomeTypeClass[Target[...]] исправить F[_] параметр test. Но с этим просто невозможноtestподпись типа. Как только у вас есть (например)

val inst = implicitly[SomeTypeClass[Target[...]]

вы можете позвонить

val res1 = inst.test[List, ...]
val res2 = inst.test[Option, ...]

Типовые лямбды не позволяют решить эту проблему. Вам нужно либо двигатьсяF[_] параметр для SomeTypeClass или реализовать

implicit def instance[F[_]]: SomeTypeClass[Target[F, *, *]] = new SomeTypeClass[Target[F, *, *]] {
  override def test[G[_], S, T, A, B](f: (A => G[B]) => S => G[T])
                                     (pab: Target[F, A, B])
                                     (implicit ev: Strong[Target[F, *, *]],
                                      ev2: Choice[Target[F, *, *]],
                                      ev3: Applicative[G]): Target[G, S, T] = ???
}

что я ожидаю невозможно, так как ты не можешь пройти pab.f к f.

РЕДАКТИРОВАТЬ: тип wander

class (Choice p, Strong p) => Traversing p where
  traverse' :: Traversable f => p a b -> p (f a) (f b)
  traverse' = wander traverse

  wander :: (forall f. Applicative f => (a -> f b) -> s -> f t) -> p a b -> p s t
  wander f pab = dimap (\s -> Baz $ \afb -> f afb s) sold (traverse' pab)

это тип ранга 2, который напрямую не поддерживается в Scala; вместо этого вам нужно ввести помощника (который не может быть просто псевдонимом типа, как вControl.Lens.Type)

trait Traversal[S, T, A, B] {
  def apply[F[_]: Applicative](f: A => F[B]): S => F[T]
}

затем

trait Traversing[P[_, _]] extends Strong[P] with Choice[P] {
  def wander[S, T, A, B](t: Traversal[S, T, A, B], pab: P[A, B]): P[S, T]
}

implicit def instance[F[_]: Applicative]: Traversing[Target[F, *, *]] = new Traversing[Target[F, *, *]] {
  def wander[S, T, A, B](t: Traversal[S, T, A, B], pab: Target[F, A, B]): Target[F, S, T] = Target(t(pab.f))
  // define Strong and Choice methods too
}

должно сработать. (Хотя я не уверен, что это кошачий способ справиться сStrong а также Choice требования.)