Есть проблема, чтобы начать с вывода C[X] = (X,X)
.Предположим, вы передаете (String, String)
куда-то, чего ожидает компилятор C[String]
, и должны вывести C
, C[X]
может быть (X, X)
, (X, String)
, (String, X)
или даже (String, String)
с фантомом X
.
Объявление псевдонима Pair
не помогает.Я полагаю, вам придется объявить case class Pair[A](_1: A, _2: A)
- само собой разумеющимся, что вывод фантома C[X] = Pair[String]
и X
все еще возможен, но, к счастью, компилятор этого не делает.
Тем не менее, когда вы пишете Tree(1, Pair(Leaf(2), Leaf(3))
, он не будет выводить C
в листьях.Я не очень уверен, почему.Но, во всяком случае, нет никакого способа сделать вывод, когда вы просто пишете val l = Leaf(2)
.
Я думаю, что вы можете добраться до чего-то, сделав все ковариантным
sealed trait Tree[+C[+X], +A]
case class Leaf[+A](a: A) extends Tree[Nothing, A]
case class Node[+C[+X], +A](a: A, c: C[Tree[C,A]]) extends Tree[C,A]
с ковариацией, вы удалите C из Leaf, поэтому нет необходимости выводить
case class Pair[+A](left: A, right: A)
val tree = Node(1, Pair(Leaf(2), Node(3, Pair(Leaf(3), Leaf(4)))))
tree: Node[Pair,Int] = Node(1,Pair(Leaf(2),Node(3,Pair(Leaf(3),Leaf(4)))))
Дополнительное замечание: не будет ли больше смысла иметь
case object Empty extends Tree[Nothing, Nothing]
вместо Leaf
.С Leaf
есть формы двоичного дерева, которые вы не можете получить.
Обновление относительно ваших комментариев:
Во-первых, не слишком заморачивайтесь с фантомным типом, я не должен был это упоминать.Если вы определяете тип T[X]
и X
не появляется иначе в определении T, это называется фантомным типом.С этим можно написать умный код, чтобы гарантировать, что некоторые свойства будут проверены во время компиляции, но здесь дело не в этом.
На самом деле, когда компилятору scala даны некоторые типы T и X, и он должен выводить C, так что C [X] является (супертипом) T - в моем примере это был T= (String, String) и X = String - это будет работать только в том случае, если T (или супертип) является параметризацией типа с ровно одним параметром типа.В более общем смысле, столько параметров типа, сколько имеет параметр типа C.Так как у C есть один, а у Tuple2 - два (вы определили псевдоним Pair, который не считается), он не может работать.
Я пытался указать, что без такого правила у компилятора было бы много вариантов для C
.Если я знаю, что (String, Int)
- это C[String]
, и я должен догадаться, что такое C
, тогда я думаю, что C[X]
- это (X, Int)
.Когда вы пишете , если передано (String, Int), не будет, если вывести (Any, Any) , это не имеет смысла, учитывая, что он пытается вывести конструктор типа.Ответ должен быть C[X] = something with X
(за исключением того, что X является фантомом).Совершенно иначе было бы иметь Pair[X] = (String, Int)
и выводить X
.Тогда действительно, это сделало бы вывод X = Any
.Таким образом, учитывая, что C [String] = (String, String), C [X] = (X, String) является таким же решением, как C [X] = (X, X).
На вашем второмкомментарий относительно List
, та же проблема существует и с Pair
после того, как вы определили case class Pair
(третий абзац в ответе выше), а именно, что он не будет выводить, что такое C
, когда вы пишете Leaf(2)
, где C
не появляется.Это где ковариация вступает в действие, отказавшись от параметра C в листе, и поэтому необходимость его вывода.