2013-08-09 2 views
1

Почему foo1 сбой и foo2 преуспевает? Разве компилятор не должен автоматически проверять все супертипы Бла?Неявное преобразование в супертип с использованием классов типов

trait Foo[A] { 
    def bar: A 
} 

trait Bleh; 
case class Blah extends Bleh; 
implicit object BlehFoo extends Foo[Bleh] 

def foo1[A:Foo](a:A) = a 

def foo2[A,B:Foo](a:A)(implicit aToB: A => B) = aToB(a) 

// Shouldn't it automatically use Bleh? 
foo1(Blah()) 
// Failure: could not find implicit value for evidence parameter of type Foo[Blah] 

foo2(Blah()) 
// Success: Bleh = Blah() 
+1

Хороший вопрос, но имена переменных, которые вы выбрали, заставляют меня хотеть рвать. Разве они не были более наглядными, как SuperClass/SubClass? – Quantum7

+0

'Foo',' Bar' и т. Д. Являются совершенно стандартными (и любимыми) в отрасли. –

ответ

6

Вы не можете использовать в качестве Foo[Bleh]Foo[Blah] поскольку Foo[Bleh] не Foo[Blah]. Вы должны сделать Foo контравариантным на A, чтобы использовать Foo[Bleh] как Foo[Blah].

trait Foo[-A] { 
    def bar(a: A) = println(a) // to make Foo contravariant 
} 

Это работает просто отлично:

scala> foo1(Blah()) 
res0: Blah = Blah() 

Ваш исходный код содержит ответ на ваш вопрос. Давайте предположим, что вы могли бы использовать оригинал Foo[Bleh] в Foo[Blah]:

def foo1[A:Foo](): A = implicitly[Foo[A]].bar 

val b: Blah = foo1[Blah]() 

В случае Foo[Bleh] используется здесь вы получите Bleh как результат bar, но вы ожидаете Blah и Bleh не Blah.

К счастью, компилятор не позволит использовать оригинал Foo[Bleh] в Foo[Blah]:

scala> trait Foo[-A] { 
    | def bar: A 
    | } 
<console>:8: error: contravariant type A occurs in covariant position in type => A of method bar 
     def bar: A 
      ^

Тип умозаключение

Это прекрасно работает:

foo1[Bleh](Blah()) 

Но компилятор не будет выводить тип параметр A здесь как Bleh. Для того, чтобы понять «почему» мы должны знать, что A:Foo означает:

def foo1[A:Foo](a:A) = a // syntax sugar 
def foo1[A](a:A)(implicit ev: Foo[A]) = a // same method 

A:Foo является синтаксисом для добавления неявного параметра.

Если у вас есть 2 группы параметров, то компилятор выдает тип первой группы и затем считает, что известные типы. Поэтому после вывода типа в первой группе параметров (a:A) тип Blah известен, а вторая группа параметров не может влиять на параметр типа.

+0

Я полностью согласен, что Foo [Bleh] отличается от Foo [Blah]. Тем не менее, экземпляр Blah по определению также является Bleh: вопросы, которые я действительно пытаюсь понять, - это то, почему в отсутствие явного Foo [Blah] компилятор не использует Blah как Bleh и разрешает класс типа правильно? – Refefer

+0

@ Refefer: Я обновил свой ответ. Если вы хотите знать, почему вывод типа работает с группами параметров один за другим, я могу дать вам две причины: 1) он полезен с lambda в качестве последнего параметра; 2) это быстрее. – senia