kind-projector возвращает странные результаты

У меня есть следующие типы: 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-projector для создания частично применяемого типа.
Моя желаемая сигнатура метода должна быть:

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] = ???
    }
  }

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


comment
Я не понимаю, что вы подразумеваете под этим использованием λ, генерирующего правильные типы для параметра. Ни один из них не компилируется, и я бы не ожидал. Если вы хотите использовать лямбда-тип в контексте SomeClass[...], вам обязательно понадобится тип с двумя параметрами *.   -  person Travis Brown    schedule 12.12.2019
comment
@TravisBrown, я обновил вопрос, указав пример параметров с двумя звездами.   -  person Sagi    schedule 12.12.2019
comment
Но λ[(F, A, B) => Target[F, A, B]] принимает три параметра, поэтому вы определенно не сможете использовать его в любом контексте, где ожидается P[_, _]. Если ваш окончательный блок кода вообще компилируется, это ошибка.   -  person Travis Brown    schedule 12.12.2019
comment
@TravisBrown Я также пробовал λ[F[_] =› Target[F, *, *]], но также получил нежелательные результаты   -  person Sagi    schedule 12.12.2019


Ответы (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)

относится к типу второго ранга которые не поддерживаются в 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.)

person Alexey Romanov    schedule 12.12.2019
comment
Так в принципе я не могу реализовать это? instance testSomeTypeClass :: Applicative f => SomeTypeClass (Target f), где test t (Target f) = Target (t f) - person Sagi; 12.12.2019
comment
Как выглядит ваш SomeTypeClass (в частности, тип test) в Haskell? - person Alexey Romanov; 12.12.2019
comment
Похоже на линзу - person Sagi; 12.12.2019
comment
test:: Applicative f =› (a -> f b) -> s -> f t -> p a b -> p s t - person Sagi; 12.12.2019
comment
Тогда у вас есть затенение и в Haskell, f в test не связано с f в instance Applicative f => SomeTypeClass (Target f) (при условии, что class SomeTypeClass p where test :: ..., а не SomeTypeClass p f). Так что test t (Target f) = Target (t f) не должен проверять тип, если только я что-то не упустил. - person Alexey Romanov; 12.12.2019
comment
И это не скомпилируется, если я попытаюсь это сделать, как и ожидалось: не удалось сопоставить ожидаемый тип «ft -> Target m a b -> Target m s t» с фактическим типом «Target f0 a1 b0» в wander f (Target amb) = Target (f amb). - person Alexey Romanov; 12.12.2019
comment
Хотя этот wander соответствует def test[F[_], S, T, A, B](f: A => F[B])(x: S, y: F[T], pab: P[A, B])(implicit ...): P[S, T] в Scala, а не подписи, которую вы указали в вопросе. - person Alexey Romanov; 12.12.2019
comment
не могли бы вы записать класс типов Traversing (просто блуждание), который соответствует SomeTypeClass, и подпись Star, которая соответствует Target из этой реализации Haskell: hackage.haskell.org/package/profunctors-5.5.1/docs/src/. Спасибо. - person Sagi; 12.12.2019
comment
Большое спасибо, я хотел бы проголосовать за ваш ответ более одного раза - person Sagi; 12.12.2019
comment
Конечно сделал. Еще до того, как вы отредактировали свой ответ с помощью файла Traversal. Просто за усилия, которые вы проявили. Еще раз спасибо - person Sagi; 13.12.2019
comment
@Sagi Нет, еще нет. Принятие отличается от голосования, вам нужно нажать на галочку под ответом. meta.stackexchange.com/questions/ 5234/ - person Alexey Romanov; 13.12.2019