2016-11-09 7 views
8

c'est donc un port assez directe de this Java question à scalaÉviter les paramètres génériques redondants dans Scala

Nous avons un tas de caractéristiques qui prennent des paramètres génériques comme suit:

trait Ident { } 

trait Container[I <: Ident] { 
    def foo(id: I): String 
} 

trait Entity[C <: Container[I], I <: Ident] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
} 

Cela fonctionne, mais il est un peu clumbsy, puisque nous devons fournir le type de l'Ident et le type du Container lors de la définition d'une sous-classe de Entity. Quand en fait seulement le type du conteneur serait assez d'informations de type par lui-même:

class MyIdent extends Ident { } 
class MyContainer extends Container[MyIdent] { } 
class MyEntity extends Entity[MyContainer,MyIdent] { } 
//          ^^^^^^^ shouldn't really be necessary 

En utilisant un type existentiel évite la nécessité d'une entité de prendre deux paramètres ... mais bien sûr vous ne pouvez pas s'y référer plus tard.

trait Entity[C <: Container[I] forSome { type I <: Ident }] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
//   ^^^ complains it has no idea what 'I' is here 
} 

De même la conversion de la chose à utiliser des types de membres ne travaillent pas non plus ...

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 
    type C <: Container 
    def container: C 
    def foo(id: C#I) = container.foo(id) 
//         ^^ type mismatch 
} 

Cela veut-il que quelqu'un sait s'il y a une solution élégante à ce problème à Scala?

+0

Je ne pense pas que la réponse soit très différente de la version Java. Il n'y a pas vraiment moyen d'omettre le paramètre type sans perdre l'information de type qui l'accompagne. –

+0

Est-ce une option pour rendre 'container' un' val'? – sepp2k

+0

@MichaelZajac Donc dans la définition de "MyEntity" le deuxième paramètre fourni à Entity est redondant: il n'y a tout simplement pas d'autre type possible que "MyIdent", littéralement tout autre chose donnera des erreurs de compilation. Bien sûr, si vous pouvez éviter cette redondance dans Scala est une autre question :-) –

Répondre

4

Mise à jourdonné this answer Je ne suis pas sûr que cela devrait être considéré comme un bug ou non

Vous avez touché SI-4377; si vous fournissez explicitement type ascriptions vous obtiendrez une erreur que je devine juste que les projections expose de type sont mises en œuvre à l'aide: existentiaux

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = (container: C).foo(id: C#I) 
    // you will get something like: type mismatch; 
    // [error] found : Entity.this.C#I 
    // [error] required: _3.I where val _3: Entity.this.C 
    // as I said above, see https://issues.scala-lang.org/browse/SI-4377 
} 

Il n'est pas un euphémisme de dire que ce (bug?) rend la programmation générique tapez membres un cauchemar.

Il est bidouillage, qui consiste à couler valeurs à un alias de type auto-référentiel fabriqué à la main:

case object Container { 

    type is[C <: Container] = C with Container { 

    type I = C#I 
    // same for all other type members, if any 
    } 

    def is[C <: Container](c: C): is[C] = c.asInstanceOf[is[C]] 
} 

utiliser maintenant et Entity compile:

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = Container.is(container).foo(id) 
    // compiles! 
} 

Ceci est bien sûr dangereux, et en règle générale, il est sûr que si C et tous ses membres de type sont liés à un type non-abstraite à le point où il sera utilisé; notez que cela ne sera pas toujours le cas, car Scala vous permet de laisser des membres de type "indéfini":

case object funnyContainer extends Container { 

    // I'm forced to implement `foo`, but *not* the `C` type member 
    def foo(id: I): String = "hi scalac!" 
} 
+1

Merci, cela fonctionne.Heureux de savoir que j'ai eu la bonne idée mais juste frappé un bug :-) –