Использование лямбда-типов вместе с типами с более высоким родом в Scala: как заставить компилятор правильно выводить типы? - PullRequest
0 голосов
/ 19 февраля 2019

Предположим, у меня есть черта, представляющая что-то вроде полиморфной функции, например:

trait Func[A[X, Y]] {
  def apply[X, Y](a: A[X, Y]): A[X, Y]
}

Теперь я хочу использовать свою черту как неполиморфную функцию, передавая тип лямбда в качестве аргумента:

type single[T] = { type t[X, Y] = T }
val aInstance: Func[single[String]#t] = 
  new Func[single[String]#t] {
    def apply[X, Y](a: String): String = ???
  }

Теперь у меня есть метод test, который делает некоторые полезные вещи с func, например,

def test[A[X, Y]](f: Func[A]): Unit = ???

И я хочу вызвать test с aInstance без указания параметров типа вручную:

test(aInstance)

К сожалению, этот код не компилируется (но test[single[String]#t](aInstance) делает) с сообщениями об ошибках:

[error] /test.scala:16:3: no type parameters for method test: (f: Func[A])Unit exist so that it can be applied to arguments (Func[[X, Y]String])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : Func[[X, Y]String]
[error]  required: Func[?A]
[error]   test(aInstance)
[error]   ^
[error] /test.scala:16:8: type mismatch;
[error]  found   : Func[[X, Y]String]
[error]  required: Func[A]
[error]   test(aInstance)
[error]        ^
[error] two errors found

Мой вопрос: как я могу изменить эти объявления, чтобы разрешить компиляторуавтоматически выводить все требуемые типы?


Тем, кто интересуется, почему я объявил Func как имеющий [X, Y], но никогда не использовал их в реальном коде, есть более реальный и менее абстрактный пример:

object GenericTest {
  trait Item { def name: String }
  class ItemA extends Item { def name: String = "a" }
  class ItemB extends Item { def name: String = "b" }

  trait MapFn[-A[X <: Item], +B[X <: Item]] {
    def apply[X <: Item](data: A[X]): B[X]
  }

  case class ItemsCollection[C[A <: Item]](a: C[ItemA], b: C[ItemB]) {
    def map[D[A <: Item]](f: MapFn[C, D]): ItemsCollection[D] =
      ItemsCollection(f(a), f(b))
  }

  // sometimes we want to store sequences...
  val itemSeq = ItemsCollection[Seq](Seq(new ItemA), Seq(new ItemB))
  // or transform them:
  val itemSet = itemSeq.map(new MapFn[Seq, Set] {
    override def apply[X <: Item](data: Seq[X]): Set[X] = data.toSet
  })

  // but one day we wanted to store just objects without any item-specific types... e.g. names:
  type single[X] = { type t[A] = X }
  val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
    override def apply[X <: Item](data: Seq[X]): String = data.head.name
  })

/*
[error] test.scala:28:27: no type parameters for method map: (f: MapFn[Seq,D])ItemsCollection[D] exist so that it can be applied to arguments (MapFn[Seq,[A]String])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : MapFn[Seq,[A]String]
[error]  required: MapFn[Seq,?D]
[error]   val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
[error]                           ^
[error] test.scala:28:31: type mismatch;
[error]  found   : MapFn[Seq,[A]String]
[error]  required: MapFn[Seq,D]
[error]   val itemNames = itemSeq.map(new MapFn[Seq, single[String]#t] {
[error]                               ^
[error] two errors found
 */
}

1 Ответ

0 голосов
/ 20 февраля 2019

Что касается вашего GenericTest, то нет способа заставить Scala вывести эту форму из-за этой закрытой, но нефиксированной ошибки .

Одна вещь, которую вы можете сделать, этопопробуйте адаптировать метод Unapply , используя неявное разрешение, чтобы определить вероятного кандидата на D.Это, вероятно, повлечет за собой определение вашего собственного класса типов и экземпляров, а не использование тех, которые поставляет Scalaz, и, возможно, изменение способа объявления MapFn как более подходящего для этого шаблона.Убедитесь, что экземпляр, который дает вам single, имеет самый низкий приоритет, так как он всегда может использоваться (каждый T является F[T], если F = Id).

Если вы контролируете определениеMapFn, вы также можете переместить параметр типа B в член типа.Тогда подпись map становится

def map(f: MapFn[C]): ItemsCollection[f.B] =

. Вы можете добавить подкласс к MapFn, который перемещает элемент типа обратно к параметру для простоты создания MapFn.

...