2014-12-14 4 views
0

Следующая не может скомпилировать с Scala 2.11.4:Озадаченный типа несоответствия при применении Scala FUNCTION1

trait Test { 
    type A 
    type F = Function1[A, String] 
} 

trait Util[T <: Test] { 
    def compute1(f: T#A => String, a: T#A): String = f(a) 
    def compute2(f: T#F, a: T#A): String = f(a) 
    //          ^
} 

Существует ошибка компиляции на аргумент вызова (^):

type mismatch; 
found : a.type (with underlying type T#A) required: _3317.A 

Я ожидал, что два аргумента f в compute1 и compute2 будут иметь один и тот же тип; очевидно нет.

Что происходит?

Как @Eugene и @ Zim-Zam ответили, проблема здесь связана с зависимыми от типа Scala типами. На основании их предложения, я придумал несколько вариантов:

trait Test { 
    type A 
    type Fa = Function1[A, String]  // original question 
    type Fb = Function1[Test#A, String] // Zim-Zam's suggestion 
} 

trait TestOps[T <: Test] { 
    type G = Function1[T#A, String] 
} 

trait Util[T <: Test] { 
    def compute1(f: T#A => String, a: T#A): String = f(a) 
    // def compute2a(f: T#Fa, a: T#A): String = f(a) 
    // type mismatch; found : a.type (with underlying type T#A) required: _1536.A 
    def compute2b(f: T#Fb, a: T#A): String = f(a) 
} 

trait Util1 { 
    def compute3a(t: Test)(f: t.Fa, a: t.A): String = f(a) 
    def compute3b(t: Test)(f: t.Fb, a: t.A): String = f(a) 
} 

trait Util2[T <: Test] { tops: TestOps[T] => 
    // def compute4a(f: T#Fa, a: T#A): String = f(a) 
    // type mismatch; found : a.type (with underlying type T#A) required: _1642.A 
    def compute4b(f: T#Fb, a: T#A): String = f(a) 
    def compute5(f: tops.G, a: T#A): String = f(a) 
} 

Util сравнивает оригинальную работу с предложением @ ЗИМ-ZAM в

Util1 упражнение разницы в указании Function1 типа аргумента: A против Test#A

Util2 упражняется в определении типа Function1 для другого признака: TestOps

Комментируя варианты, не задающие проверку, у нас осталось:

compute1 compute2b compute3a compute3b compute4b compute5

Что лучше для специализируя эти черты?

чесать различия, я сделал простое уточнение:

class U 

class TestU extends Test { 
    override type A = U 
} 

class UOps extends TestOps[TestU] 

Вот результаты:

trait UtilU extends Util[TestU] { 
    def get1(f: TestU#A => String, a: TestU#A) = compute1(f, a) 
    // def get2b(f: TestU#A => String, a: TestU#A) = compute2b(f, a) 
    // type mismatch; found : A.U ⇒ String required: A.Test#A ⇒ String 
} 

trait UtilU1 extends Util1 { 
    val u = new TestU() 
    def get3a(f: u.A => String, a: u.A) = compute3a(u)(f, a) 
    // def get3b(f: u.A => String, a: u.A) = compute3b(u)(f, a) 
    // type mismatch; 
    // found : UtilU1.this.u.A ⇒ String (which expands to) A.U ⇒ String 
    // required: UtilU1.this.u.Fb (which expands to) A.Test#A ⇒ String 
} 

class UtilU2 extends Util2[TestU] with TestOps[TestU] { 
    // def get4b(f: TestU#A => String, a: TestU#A) = compute4b(f, a) 
    // type mismatch; found : A.U ⇒ String required: A.Test#A ⇒ String 
    def get5(f: TestU#A => String, a: TestU#A) = compute5(f, a) 
} 

Итак, мы оставили только три разумных вариации:

compute1 (нет псевдонима функции1) compute3a compute5

Для наложения типов FUNCTION1, мы остались только 2 альтернативы: compute3a и compute5

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

ответ

3

@Eugene правильно об этом существе вызванные зависимые пути типов - вы можете это исправить с

trait Test { 
    type A 
    type F = Function1[Test#A, String] 
} 

Сейчас F может принимать любые A в качестве аргумента

+0

другой вариант - определить F внутри Util –

+0

Спасибо @ Zim-Zam за предложение. Однако этот подход вызывает проблемы при определении специализаций теста признаков, поскольку типы аргументов функции являются противоположными вариантами в Scala. –

4

Это из-за способа, как путь-зависимые типы, реализованные в Скале

val test1 = new Test {} 
val test2 = new Test {} 
// test1.A is different type from test2.A 

в вашем примере вы в основном говорят, что е и может передаваться от различных экземпляров теста, и в этом случае используется в типе F в первом аргументе будет отличаться от второго аргумента A.

Но если вы будете ограничивать его в какой-то экземпляр T будет компилировать

def compute2(t: T)(f: t.F, a: t.A): String = f(a) 

U pd, Однако он по-прежнему должен быть для compute1

+0

Благодаря @Eugene для объяснения (зависимые от пути типы). Этот подход кажется непрактичным, потому что я должен передать экземпляр T как аргумент, который нельзя сделать неявным. –