2016-03-25 1 views
0

J'utilise des types abstraits et je rencontre des problèmes pour que le compilateur Scala unifie deux types qui doivent être identiques. Ce sont mes définitions:Incompatibilité de type avec les types abstraits

trait Chromosome { 
    type Gene 

    val size : Int 

    def apply(idx : Int) : Gene 

    def update(idx : Int, g : Gene) 

    def indices = Range(0, size-1) 
} 


trait Individual { 
    type Fitness 
    implicit protected val ord: Ordered[Fitness] = implicitly[Ordered[Fitness]] 

    val chromosome : Chromosome 

    def size : Int = chromosome.size 

    def apply(idx : Int) : chromosome.Gene = chromosome.apply(idx) 
    def update(idx : Int, g : chromosome.Gene) = chromosome.update(idx, g) 

    protected var _fitness : Fitness = _ 
    def fitness : Fitness = _fitness 
    def fitness_=(f : Fitness):Unit = _fitness = f 
} 


case class ArrayChromosome(size : Int) extends Chromosome { 
    implicit protected val tag : ClassTag[Gene] = implicitly[ClassTag[Gene]] 

    protected val xs : Array[Gene] = new Array[Gene](size) 

    def apply(idx : Int) = xs(idx) 

    def update(idx : Int, g : Gene) = xs(idx) = g 
} 


abstract class ArrayIndividual(size : Int) extends Individual { 
    val chromosome = ArrayChromosome(size) 
} 


class MyIndividual(size : Int) extends ArrayIndividual(size) { 
    type Gene = Int 
    type Fitness = Double 
} 


object Test extends App { 
    val i1 = new MyIndividual(10) 

    i1.fitness = 10.5 
    i1.chromosome(0) = 6 
    print(i1) 
} 

A savoir, le problème est dans cette ligne:

i1.chromosome(0) = 6 

et l'erreur de type est:

Error:(75, 22) type mismatch; 
found : Int(6) 
required: abstractTypes.Test.i1.chromosome.Gene 
    i1.chromosome(0) = 6 

Il semble que le compilateur ne parvient pas à unifier i1.chromosome.Gene avec Int.

J'ai deux questions:

  1. Est-il possible d'aider le compilateur à unifier les deux types? Permettez-moi maintenant de supposer que j'ai supprimé la ligne fautive et que j'ai défini correctement toString pour Individual s. Pourquoi puis-je obtenir un java.lang.NullPointerException lors de l'exécution Test. Il semble que l'erreur est liée à GeneClassTag, mais je ne suis pas sûr.

Code de réponse à @ som-snytt:

abstract class ArrayIndividual(size : Int) extends Individual { 
    type Gene 
    implicit protected def tag2: ClassTag[Gene] 

    val chromosome = new ArrayChromosome(size) { 
    type Gene = ArrayIndividual#Gene 
    protected val tag : ClassTag[Gene] = tag2 
    } 
} 

class MyIndividual(size : Int) extends ArrayIndividual(size) { 
    type Fitness = Double 
    type Gene = Int 
    implicit protected val tag2 : ClassTag[Gene] = implicitly[ClassTag[Gene]] 

    override def toString = s"${super.toString} $chromosome" 
} 

Répondre

1

En Scala, les types ne sont pas globalement SCOPED. Vous avez écrit Gene en tant que membre du type Chromosome, mais vous essayez de le définir dans une sous-classe de Individual (qui "a un" Chromosome, pas "est un" Chromosome).

Vous pouvez choisir où déclarer et le définir, mais il doit être dans la même hiérarchie, tout comme lorsque vous remplacez un membre de terme tel qu'une méthode. Un individual.Gene n'a rien à voir avec un chromosome.Gene dans votre exemple.

Déplacer la définition du type où vous avez besoin pour la ClassTag de toute façon:

case class ArrayChromosome(size : Int) extends Chromosome { 
    type Gene = Int 
    implicit protected val tag : ClassTag[Gene] = implicitly[ClassTag[Gene]] 

D'oh, je viens de remarquer que vous définissez votre récursive implicite. L'implicitement sur le RHS utilise l'implicite que vous définissez. Alors ne fais pas ça. Le compilateur appelle l'implicite lui-même. Mais si vous avez besoin de fournir une balise implicite dans une superclasse qui n'a pas corrigé le type d'alias, vous pouvez avoir une méthode abstraite pour fournir une balise de classe qui serait implémentée dans une classe concrète.

Par exemple, en laissant une sous-classe Individual spécifier le béton Chromosome:

abstract class ArrayChromosome(val size : Int) extends Chromosome { 
    //type Gene = Int 
    //implicit protected val tag : ClassTag[Gene] = implicitly[ClassTag[Gene]] 
    implicit protected def tag: ClassTag[Gene] 

    protected lazy val xs : Array[Gene] = new Array[Gene](size) 

    def apply(idx : Int) = xs(idx) 

    def update(idx : Int, g : Gene) = xs(idx) = g 
} 


abstract class ArrayIndividual(size : Int) extends Individual { 
    //val chromosome = ArrayChromosome(size) 
    val chromosome = new ArrayChromosome(size) { 
    type Gene = Int 
    protected val tag = implicitly[ClassTag[Gene]] 
    } 
} 


class MyIndividual(size : Int) extends ArrayIndividual(size) { 
    type Fitness = Double 

    override def toString = s"${super.toString} $chromosome" 
} 
+0

Merci pour votre réponse, mais le type gène doit être abstraite en classe ArrayChromosome et ne se défini plus loin dans les sous-classes étendant celui-ci. Je suppose que je pourrais ajouter un paramètre de type, mais est-il possible de résoudre le problème d'unification sans un tel paramètre? – pepeStck

+0

C'est ce que je voulais dire par le commentaire sur tag. Je vais ajouter un exemple de code. –

+0

Notez le val paresseux. –