2015-10-23 1 views
1

J'expérimente des variables de type et des types structurels dans le cadre de l'apprentissage du modèle de gâteau de Scala.Si les méthodes définies dans le type structurel 'B' sont un surensemble de type structurel 'A', pourquoi ne puis-je pas passer un 'B' à une méthode qui veut un 'A'?

Ci-dessous est une API de jouet qui illustre ma question:

Lorsque les méthodes définies dans 'B' de type structurual sont plus nombreuses que celles de type structurual 'A', pourquoi ne puis-je passer une instance de B à une méthode qui veut un «A»?

... Toy API .... 

object Tester extends App { 

    trait SomeApi { 
    type Organism <: { 
     def die(): Unit; 
    } 

    type Dog <: { 
     def die(): Unit; 
     def bark(): Unit; 
    } 

    def dieQuietlyDoesntCompile(entity: Organism): Unit = { 
     entity.die() 
    } 

    def dieQuietly(entity: { def die(): Unit }): Unit = { 
     entity.die() 
    } 

    def processDog(dog: Dog): Unit = { 
     println("start dog process on : " + dog) 
     dieQuietly(dog)        
    } 
    } 
} 

Les types de structure dans mon API commencer avec ce que vous pourriez (organisme dans l'exemple ci-dessus) appeler un « type de base », plus, je ai d'autres types de l'API, qui étendent le type de base. Dans le cas de l'API Toy, Dog a toutes les méthodes de l'organisme, plus un de plus: 'bark()'. Je veux écrire quelques méthodes auxiliaires qui fonctionnent sur les types de base comme cela est illustré par la méthode processDog() .... qui prend une instance 'Dog', mais qui veut aussi appeler 'dieQuietly' gère le type plus générique 'Organisme'.

La façon dont j'ai fait les choses ci-dessus fonctionne, mais il est vraiment maladroit parce que je dois entièrement répéter toutes les méthodes de le type structurel de base. Pas si mal dans ce cas de jouets (puisque je n'ai qu'une seule méthode: die()), mais vraiment gênant car le nombre de méthodes dans ces types structurels augmente de . Par conséquent, je préfère passer l'instance de chien à une méthode écrite comme 'dieQuietlyDoesntCompile()'.
Mais que les fonctions nom l'indique, si je passe un exemple de chien, il ne parvient pas à compiler avec l'erreur:

type mismatch; found : dog.type (with underlying type SomeApi.this.Dog) required: SomeApi.this.Organism

Quelqu'un peut-il suggérer une façon plus pratique pour atteindre mon but ...? Ou suis-je coincé répéter les méthodes dans le type de base? (une approche qui ne me semble pas très sèche). Merci d'avance pour votre aide !/chris

Répondre

2

Votre problème est que vous utilisez le type lié <: pour définir Dog et Organism. Vous les contraignez en tant que sous-classes de la classe avec la méthode die(), ce qui les rend non apparentés.

Permettez-moi d'illustrer à l'aide de types réguliers:

trait Mortal // suppose this trait is analogue of { def die():Unit } 
class Organism extends Mortal 
class Dog extends Mortal 

def die(o:Organism) {} 

die(new Dog) // obviously will not compile 

Votre code peut être facilement fixé en définissant Organism sans bornes de type:

type Organism = { 
    def die(): Unit; 
    } 
+0

bons points. malheureusement, j'ai besoin de comprendre comment travailler avec des types structurels et non des classes, comme je suis la programmation contre une base de code existante. Cette base de code est en fait de la classe coursera sur Akka. Je ne sais pas combien de temps durera le lien, mais ici il est en entier: https://github.com/tonyskn/coursera-reactive/blob/master/w4-suggestions/src/main/scala/suggestions/gui/SwingApi. scala - et voici un autre SO question sur ce même code: http://stackoverflow.com/questions/20508529/subtype-in-scala-what-is-type-x-y –

+0

@ChrisBedford, je pense avoir répondu à vos questions. 'pourquoi ne puis-je pas passer une instance de B à une méthode qui veut un 'A'?' Aivean

0

Pour développer un peu sur la réponse de Aivean:

When methods defined in structurual type 'B' are a superset of those in structurual type 'A', why can't I pass an instance of B to a method that wants an 'A'?

Vous pouvez, mais dans ce cas, ils ne le sont pas. Organism n'est pas un type structurel, c'est un membre de type abstrait qui doit être un sous-type d'un type structurel.Une mise en œuvre possible de SomeApi est

class ApiImpl extends SomeApi { 

    type Organism = { 
    def die(): Unit 
    def live(): Unit 
    } 

    type Dog = { 
    def die(): Unit 
    def bark(): Unit 
    } 

    override def dieQuietlyDoesntCompile(entity: Organism): Unit = { 
    entity.live() 
    } 
} 

Espérons qu'il est évident pourquoi vous ne pouvez pas passer un Dog à dieQuietlyDoesntCompile ici; mais cela signifie également que vous ne pouvez pas le passer dans le cas d'origine, ou vous pouvez le faire

val api: SomeApi = new ApiImpl 

val dog: api.Dog = ... 

api.dieQuietlyDoesntCompile(dog)