2013-06-23 1 views
6

Предположим, есть черта:Особенности различных видов пути-зависимых типов в Скале

trait OuterTrait { 
    type InnerType 
} 

Теперь мы можем написать необщего функцию someAlgo:

def pairToString[S, U](x: S, y: U): String = 
    "{" + y.toString + " in " + x.toString + "}" 

def pairPrintln[S, U](x: S, y: U) { 
    println(pairToString(x, y)) 
} 

def someAlgo(x: OuterTrait)(y: x.InnerType): x.InnerType = { 
    pairPrintln(x, y) 
    y 
} 

и ряд общих функций:

def someAlgoObjObj[T <: OuterTrait](x: T)(y: x.InnerType): x.InnerType = { 
    pairPrintln(x, y) 
    y 
} 

def someAlgoObjType[T <: OuterTrait](x: T)(y: x.InnerType): T#InnerType = { 
    pairPrintln(x, y) 
    y 
} 

def someAlgoTypeType[T <: OuterTrait](x: T)(y: T#InnerType): T#InnerType = { 
    pairPrintln(x, y) 
    y 
} 

И еще одна общая функция не компилируется:

def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = { 
    pairPrintln(x, y) 
    y 
} 

Кажется, что: 1) someAlgo и someAlgoObjObj являются наиболее правильными функциями; 2), и нет смысла использовать общую функцию в этом примере вообще.

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

Так как я понимаю тип T соответствует статического типа x (назовем его X) или явного типа общего вызова (я имею в виду algo[Int], например). Вот почему T#InnerType соответствует типу в объявлении типа X. Но x.InnerType также соответствует InnerType от статический тип x. Где разница?

Далее ... someAlgoObjType компилирует, так что кажется, что x.InnerType должен быть подтип из T#InnerType. Тогда это нормально, что someAlgoTypeObj не компилируется, так как мы не можем сделать утапливание неявно. Хотя мы можем переписать последний:

def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = { 
    pairPrintln(x, y) 
    y.asInstanceOf[x.InnerType] 
} 

UPD1: Я нашел одно различие между someAlgoObjObj и someAlgoTypeType, если их использовать с параметром явного типа. Если мы напишем некоторый класс, расширяющий OuterTrait:

class OuterIntClass extends OuterTrait{ 
    type InnerType = Int 
} 
val x: OuterIntClass = new OuterIntClass() 
val y: Int = 5 

Тогда:

someAlgoObjObj[OuterTrait](x)(y) // OK 

и следующий звонок не работает:

someAlgoTypeType[OuterTrait](x)(y) 
+0

есть большой [Блогпост] (http://danielwestheide.com /blog/2013/02/13/the-neophytes-guide-to-scala-part-13-path-dependent-types.html) на PDT, вы обязательно должны это прочитать – 4lex1v

+0

Спасибо, это интересно. Но у меня все еще есть проблема с 'T # InnerType' ... – juliet

ответ

0

T#InnerType означает «InnerType принадлежность в некоторых T ", а x.InnerType означает" A InnerType, принадлежащий в заданном x (типа OuterTrait) ".

Ключ здесь в понимании их в некотором Т против в заданном х. Вы можете интерпретировать в некотором как некоторых T, но мы не имеем то, что T instance, что означает, что в двух Ts не обязательно одинаковы, поэтому T # InnerType не может быть доказано равным другому T #InnerType.

Давайте проанализируем подпись:

/* 1 */ def someAlgoObjObj[T <: OuterTrait](x: T)(y: x.InnerType): x.InnerType = ??? 
/* 2 */ def someAlgoObjType[T <: OuterTrait](x: T)(y: x.InnerType): T#InnerType = ??? 
/* 3 */ def someAlgoTypeType[T <: OuterTrait](x: T)(y: T#InnerType): T#InnerType = ??? 
  1. Учитывая е и InnerType принадлежности в это х возвращает его InnerType.
  2. Учитывая й и InnerType принадлежащего в этого х возвращает InnerType, принадлежащего некоторого Т, что означает некоторого Т не обязательно тот же экземпляр, как х.
  3. Учитывая й и InnerType, принадлежащий некоторого T возвращает InnerType принадлежащего некоторого Т

Теперь четвёртые один:

def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = y 

Подписи гласит: даны х и InnerType принадлежащих к некоторые T, возвращает InnerType, принадлежащий , этому x. Но в реализации мы пытаемся вернуть y, который принадлежит T, который не обязательно совпадает с x, поэтому компилятор жалуется.

+1

Но _some type_' T' распространяется на всю подпись функции. Таким образом, мы имеем _some type_ 'T' и параметр' x' _this некоторый тип_ 'T'. Вы говорите: «Учитывая, что x и InnerType, принадлежащие этому x, возвращают InnerType, принадлежащий некоторому T, что означает, что некоторый T не обязательно является тем же самым экземпляром, что и x». Не то же самое - может быть, но тип 'x' должен быть подтипом' T'. – juliet

+1

Я имею в виду, что когда мы вызываем некоторый экземпляр общей функции, мы имеем один и тот же экземпляр 'Q'' T' всюду в нашей функции. И так как 'x: Q',' x.InnerType' не может абсолютно отличаться от 'Q # InnerType'. – juliet

+0

Вам не хватает точки. В '(x: T) (y: T # InnerType)', Ts здесь обозначает разные экземпляры - не обязательно разные, но компилятор не может сделать это различие. Компилятор может полагаться только на информацию, которую вы предоставляете, и в этом случае, поскольку x.InnerType и y могут быть разными, он предполагает, что это факт. – pedrofurla

0

Небольшая заметка о обновление.

someAlgoTypeType[OuterTrait](x)(y) 

Failes, потому что ваш метод подписи говорит, что он ожидает, что его параметр y соответствовать типу T#InnerType и ваш y.type является Int. Для того, чтобы сделать его работу, вы должны изменить его тип к следующему:

class OuterIntClass extends OuterTrait{ 
    type InnerType = Int 
} 
val x: OuterIntClass = new OuterIntClass() 
val y: x.InnerType = 5 

Теперь типа проекции удовлетворяет типа y «s T#InnerType и someAlgoTypeType[OuterTrait](x)(y) компилирует