2014-09-17 4 views
2

Предположим, что у меня есть экземпляр произвольного однопараметрического класса (я буду использовать в демонстрации, но это может мне другое другое).Scala: вывод типа общего типа и аргумент типа

Я хотел бы написать обобщенную функцию, которая может принимать экземпляры (c) и быть в состоянии понять, что общий класс (A) и какой тип аргумент (B), полученный класс (C) этот экземпляр.

я придумал что-то вроде этого (тело функции не очень актуальным, но демонстрирует, что C соответствует A[B]):

def foo[C <: A[B], A[_], B](c: C) { 
    val x: A[B] = c 
} 

... и он компилирует, если вы вызываете его, как это :

foo[List[Int], List, Int](List.empty[Int]) 

... но компиляция завершается с ошибкой, если я опускаю аргументы явных типа и полагаться на умозаключения:

foo(List.empty[Int]) 

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

Error:Error:line (125)inferred kinds of the type arguments (List[Int],List[Int],Nothing) do not conform to the expected kinds of the type parameters (type C,type A,type B). 
List[Int]'s type parameters do not match type A's expected parameters: 
class List has one type parameter, but type A has one 
    foo(List.empty[Int]) 
^
    Error:Error:line (125)type mismatch; 
found : List[Int] 
required: C 
    foo(List.empty[Int]) 
       ^

Как вы можете видеть вывод типа Scala не может правильно вывести типы в этом случае (кажется, что это догадка List[Int] вместо List для 2-го аргумента и Nothing вместо Int для 3-го).

Я предполагаю, что ограничения типа для foo Я придумал, недостаточно точные/правильные, поэтому мой вопрос заключается в том, как я мог его реализовать, поэтому Scala могла вывести аргументы?

Примечание: если это помогает, предполагается, что все потенциальные дженерики (A s) наследуют/соответствуют некоторым общим предкам. Например, A может быть любой коллекцией, унаследованной от Seq.

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

ответ

1

В дополнение к hubertp ответ, вы можете исправить вы действуете, удаляя устаревшие (в вас пример) тип переменной C, например:

def foo[A[_], B](c: A[B]) { 
    val x: A[B] = c 
} 

В этом случае scalac бы вывести A[_], как List и B как Int.

Обновление (согласно комментарию).

Если вам нужно доказательство того, что C является подтипом A[B], а затем использовать неявный:

def foo[A[_], B, C](c: C)(implicit ev: C <:< A[B]) = { 
    val x: A[B] = c 
} 

Тогда он не будет компилировать это:

scala> foo[List, String, List[Int]](List.empty[Int]) 
<console>:9: error: Cannot prove that List[Int] <:< List[String]. 
       foo[List, String, List[Int]](List.empty[Int]) 
+0

посмотреть мой комментарий на hubertp ответить –

+0

@EugenyLoy проверить мое обновление, это то, что вам нужно? Но scalac не будет декомпрессировать C в A [_] и B, вы можете написать ребятам из вилки Scale типа scala, возможно, они будут реализовывать ее. – 4lex1v

+0

Да, это самое близкое к тому, что мне нужно. BTW, '=: =' лучше подходит для моих целей, чем '<: <', но у меня есть идея, и это не так важно. –

3

Это известное ограничение для вывода типа Scala для конструкторов типов. Определение типа формального параметра c как C только собирает ограничения типа для C (и косвенно A), но не B. Другими словами List[Int] <: C => { List[Int] <: C <: Any, C <: A[_] <: Any }.

Существует довольно простой перевод, который позволяет вести вывод типа для таких случаев. В вашем случае это:

def foo[C[_] <: A[_], A[_], B](c: A[B]) { val x: A[B] = c } 

Одинаковая семантика, только немного отличающаяся подпись типа.

+0

Спасибо за ответ, но это не на самом деле решает проблему. Я начинаю с аргумента типа 'C' (который, как я знаю, является результатом' A [B] ') и хочу разложить его на типы, из которых он был построен (т. Е. B и A). Как указывал @ 4lex1v в вашем примере, 'C' вообще не используется, и мне нужно иметь возможность использовать все 3 типа внутри функции (т. Е. Тело должно знать, что аргумент имеет тип' C' и соответствует ' A [B] ') –

+0

А, да. В таком случае вы после чего-то следуете по поводу неявного взлома Unpack, упомянутого в [билет SI-2712] (https://issues.scala-lang.org/browse/SI-2712)? – hubertp

Смежные вопросы