Scala Тип-вывод для конструктора типов - PullRequest
8 голосов
/ 03 декабря 2011

У меня вопрос по выводу типов в конструкторах типов Scala. Я использую Scala 2.9.1 ...

Предположим, я определил дерево:

 sealed trait Tree[C[_], A]
 case class Leaf[C[_], A](a: A) extends Tree[C, A]
 case class Node[C[_], A](a: A, c: C[Tree[C, A]]) extends Tree[C, A]

И определил BinaryTree на основе моего определения дерева:

 type Pair[A] = (A, A)
 type BinaryTree[A] = Tree[Pair, A]

Теперь я могу определить BinaryTree целых чисел следующим образом:

 val tree: BinaryTree[Int] = Node[Pair, Int](1, (Leaf(2), Leaf(3)))

Проблема в том, что я должен указывать параметры типа всякий раз, когда создаю экземпляр Node.

Так что, если сделать это:

 val tree: BinaryTree[Int] = Node(1, (Leaf(2), Leaf(3)))

Я получаю ошибку:

error: no type parameters for method apply: (a: A, c: C[Tree[C,A]])Node[C,A] in 
object Node exist so that it can be applied to arguments (Int, (Leaf[Pair,Int], Leaf[Pair,Int]))
 --- because ---
 argument expression's type is not compatible with formal parameter type;
 found   : (Leaf[Pair,Int], Leaf[Pair,Int])
 required: ?C[Tree[?C,?A]]
   val tree: BinaryTree[Int] = Node(1, (Leaf(2), Leaf(3)))
                               ^

Можно ли как-нибудь принудить средство проверки типов, чтобы мне не приходилось явно указывать типы Node?

Спасибо!


<Ч /> Пересмотрено после комментариев дидье

Если я правильно понимаю, утверждение

 type Pair[A] = (A, A)

в моем исходном вопросе не работает, так как это объявление Pair является просто синтаксическим сахаром для конструктора типа Tuple2 (для которого требуются два параметра типа). Это приводит к сбою вывода типа.

Если я объявлю свой собственный класс Pair (как предполагает Дидьерд в своем ответе), я добьюсь успеха в том, чтобы заставить дерево работать правильно.

// Assume same Tree/Leaf/Node definition given above
case class MyPair[A](_1: A, _2: A)
type BinaryTree[A] = Tree[MyPair, A]

Тогда я могу сделать это ...

scala> val t: BinaryTree[Int] = Leaf(3)
t: BinaryTree[Int] = Leaf(3)

scala> val t2: BinaryTree[Int] = Node(1, MyPair(Leaf(2), Leaf(3)))
t2: BinaryTree[Int] = Node(1,MyPair(Leaf(2),Leaf(3)))

Я знаю, что Дидир упомянул это решение мимоходом, но, похоже, оно ведет себя так, как я хочу. Пожалуйста, дайте мне знать, что вы думаете!

Ответы [ 2 ]

6 голосов
/ 03 декабря 2011

Есть проблема, чтобы начать с вывода 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]

вместо LeafLeaf есть формы двоичного дерева, которые вы не можете получить.


Обновление относительно ваших комментариев:

Во-первых, не слишком заморачивайтесь с фантомным типом, я не должен был это упоминать.Если вы определяете тип 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 в листе, и поэтому необходимость его вывода.

3 голосов
/ 03 декабря 2011

Единственное, что мне пришло в голову, - это аннотировать аргумент Pair. У других людей, я уверен, будут другие идеи.

object BinaryTreeTest {
  sealed trait Tree[C[_], A]
  case class Leaf[C[_], A](a: A) extends Tree[C, A]
  case class Node[C[_], A](a: A, c: C[Tree[C, A]]) extends Tree[C, A]

  type Pair[A] = (A, A)
  type BinaryTree[A] = Tree[Pair, A]

  val p: Pair[Tree[Pair, Int]] = (Leaf(2), Leaf(3))    
  val t: BinaryTree[Int] = Node(1, p)    
}
...