2015-05-05 2 views
3

Я просто пытаюсь окунуться в систему типа Scala. Я наткнулся на код, который выглядит следующим образом:Scala рекурсивные типы

trait A extends Something { 
    type X <: XLike 

    trait XLike { this: X => 
    .... 
    .... 
    } 
} 

Что бы это значило? В каких ситуациях я бы хотел написать такой код? Я знаю, что я вложенные типы, но в чем преимущество типов вложенности, подобных этому, и используя это для обозначения вложенного типа?

ответ

6

Эта A#XLike черта не могут быть смешаны в любом месте внутри A:

val a = new A {}; import a._ 

scala> class KK extends XLike 
<console>:21: error: illegal inheritance; 
self-type KK does not conform to a.XLike's selftype a.X 
     class KK extends XLike 
         ^

можно смешивать внутри A сек реализации:

trait B extends A { 
    type X = XImpl  
    trait XImpl extends XLike { this: X => } 
} 

val b = new B {}; import b._ 

scala> class KK extends XImpl 
defined class KK 

Или даже:

trait B extends A {type X = XLike} 

val b = new B {}; import b._ 

scala> class KK extends XLike 
defined class KK 

Так что позволяет вы можете выбрать, какая черта обязательна для смешивания:

trait B extends A { 
    type X = XImpl1  
    trait XImpl1 extends XLike { this: X => } 
    trait XImpl2 extends XLike { this: X => } 
} 

val b = new B {}; import b._ 

scala> class ZZ extends XImpl1 
defined class ZZ 

scala> class ZZ extends XImpl2 
<console>:40: error: illegal inheritance; 
self-type ZZ does not conform to b.XImpl2's selftype b.XImpl2 with b.X 
     class ZZ extends XImpl2 
         ^
scala> class ZZ extends XImpl1 with XImpl2 // XImpl2 still can be mixed but only if XImpl1 present in linearization 
defined class ZZ 

Даже несколько из них:

trait A { 
    type X1 <: XLike 
    type X2 <: XLike 

    trait XLike { this: X1 with X2 => } 
} 

trait B extends A { 
    type X1 = XImpl1 
    type X2 = XImpl2 

    trait XImpl1 extends XLike { this: X1 with X2 => } 
    trait XImpl2 extends XLike { this: X1 with X2 => } 
    trait XImpl3 extends XLike { this: X1 with X2 => } 
} 

На практике X1 и X2 может иметь некоторые важные роли. Например, если вы выполняете цепочку resposibilty (stackable traits) - вы можете смешивать многие черты (и не все из них явно - могут быть группы признаков) в любом порядке, поэтому хорошо иметь возможность указывать одну или несколько обязательные обработчики (только не забывать их), как:

val a = new Logic with Hanler with Group0 
trait Group0 extends MandatoryHandler1 with Group1 with H2 
trait Group1 extends H3 with H4 with MandatoryHandler2 
trait Group2 extends H2 with H5 

Если изменить Group0 к Group2 вы можете потерять ваш MandatoryHandler2 (это может быть какой-то репортер, например).

Еще один: разработчик библиотеки может предоставить множество черт, чтобы быть лаконичным с принципом Single Resposibilty/Interface Segregation, но некоторые из них всегда должны быть смешаны (вместе) пользователями, которые будут играть с этим LEGO.

Так вот классический OOP-composition над агрегирования способом (требуется экземпляров):

abstract class B { 
    val helper1: H1 //mandatory, exactly H1, not some H 
    val helper2: H2 
} 

class C extends B { 
    val helper1 = new H1 //mandatory, exactly H1, not some H 
    val helper2 = new H2 
} 
class H1 extends H {...} 
class H2 extends H {...}  

против mix-in образом:

trait B extends A { 
    type helper1 = H1 
    type helper2 = H2 

    trait H1 extends H // no abstract members here just mix-in 
    trait H2 extends H 
} 

trait C extends H1 with H2 
+0

Что может быть возможные случаи использования? Зачем мне все это делать? – sparkr

+1

«Я наткнулся на код, который выглядит так» - как насчет этого. Не беспокойтесь, никакой драмы здесь, поскольку код очень прост - вы просто говорите, что одна черта всегда должна быть смешана с другой - это просто дополнительная проверка времени компиляции. – dk14

+0

Например, когда вы выполняете цепочку resposibilty - вы можете смешивать более 9000 признаков, поэтому хорошо иметь возможность указывать один или несколько обязательных обработчиков, например 'val a = new Logic with Hanler with OtherHandlerList; trait OtherHandlerList расширяет MandatoryHandler с H1 с H2' - и это только первое, что пришло в голову. – dk14

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