2016-02-26 3 views
4

Я столкнулся с какой-то странной ситуацией в Scala сегодня, в то время как я попытался уточнить границы типов для элемента абстрактного типа.Scala Элементы абстрактного типа - наследования и ограничения типов

У меня есть две черты, которые определяют границы элемента типа и объединяют их в конкретном классе. Это отлично работает, но при согласовании/литья с комбинацией признака только один из двух TypeBounds является «активным», и я изо всех сил, чтобы понять, почему ...

Я попытался подготовить пример:

trait L 
trait R 

trait Left { 
    type T <: L 
    def get: T 
} 

trait Right { 
    type T <: R 
} 

прямо сейчас если объединить эти две черты в одном конкретном классе

val concrete = new Left with Right { 
    override type T = L with R 
    override def get: T = new L with R {} 
} 

я могу получить доступ к члену через ГЭТ по назначению

// works fine 
val x1: L with R = concrete.get 

, но если я набрал (слева направо) или совпадение с образцом, я больше не могу получить доступ к элементу. В зависимости от порядка я получаю либо границы типа слева, либо справа, но не комбинацию обоих.

// can only access as R, L with R won't work 
val x2: R = concrete.asInstanceOf[Left with Right].get 

// can only access as L, L with R won' work 
val x3: L = concrete.asInstanceOf[Right with Left].get 

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

может кто-нибудь пролить свет на то, почему это происходит?

ответ

8

элемент второго типа переопределяет первый.

trait L 
trait R 

trait Left { 
    type T <: L 
    def get: T 
} 

trait Right { 
    type T <: R 
} 

object X { 
    type LR = Left with Right // Right#T overrides Left#T, LR#T is now <: R 
    type RL = Right with Left // Left#T overrides Right#T, LR#T is now <: L 

    val concrete = new Left with Right { 
    override type T = L with R 
    override def get: T = new L with R {} 
    } 

    // ok 
    val r: R = concrete.asInstanceOf[LR].get 
    val l: L = concrete.asInstanceOf[RL].get 

    // ok 
    implicitly[LR#T <:< R] 
    implicitly[RL#T <:< L] 

    // doesn't compile, LR#T is a subclass of R because Right#T overrides Left#T 
    implicitly[LR#T <:< L] 
    // doesn't compile, RL#T is a subclass of L because Left#T overrides Right#T 
    implicitly[RL#T <:< R] 
} 

В «бетон» переопределить элемент типа с L with R, но когда вы приводите его к Left with Right вы теряете, что утонченность, и T становится _ <: L или _ <: R в зависимости от порядка черт ,

Поскольку члены типа могут быть переопределены, если вы повышаетесь (например, до LR или RL), вы теряете уточнение, которое вы применяете в бетоне. Вы могли бы сказать, что конкретное является одновременно RL и LR, но когда вы повышаете его до LR или RL, вы теряете информацию, имеющуюся у вас в другом

+0

Вы можете узнать больше о том, почему теперь scala переопределяет типы и как это можно сделать по-другому в этой концепции для следующего поколения scala http://www.cs.uwm.edu/~boyland/fool2012/papers/fool2012_submission_3.pdf – Odomontois

+0

I haven У меня было время погрузиться в точку, но я определенно с нетерпением жду этого! –

+1

Вы можете увидеть немного о будущем awesomeness в этом видео https://www.youtube.com/watch?v=WxyyJyB_Ssc. Замечания о бесформенном включены – Odomontois

3

Кроме ответа TrustNoOne я могу предложить следующее обходное решение с некоторыми ограничениями , Вы можете создать свой собственный комбинатор типов вместо with для преодоления переопределения типов.

trait L 
    trait R 
    trait Base { 
    type T 
    def get: T 
    } 
    trait Nil extends Base{ 
    type T = Any 
    } 
    trait ~+~[X[_ <: Base] <: Base, Y[_ <: Base] <: Base] extends Base { 
    type T = Y[X[Nil]]#T 
    } 
    trait Left[B <: Base] extends Base { 
    type T = B#T with L 
    } 
    trait Right[B <: Base] extends Base { 
    type T = B#T with R 
    } 
    val concrete = new (Left ~+~ Right) { 
    def get: T = new L with R {} 
    } 

    val x1: L with R = concrete.get 
    val x2: R = concrete.asInstanceOf[Left ~+~ Right].get 
    val x3: L = concrete.asInstanceOf[Right ~+~ Left].get 

Этот код в настоящее время успешно компилируется, заметь, однако, что мы не можем объединить пространство имен комбинированных типов в новом типе, поэтому все известные методы должны быть определены в Base или полученных в некотором классе типов через механики похожи на бесформенную HList

+0

спасибо также вам, его удивительный, что Scala может сделать! –

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