2012-04-27 3 views
6

Ce problème est survenu dans un module que j'écris, mais j'ai fait un cas minimal qui présente le même comportement.Pourquoi le type d'inférence ne fonctionne-t-il pas ici?

class Minimal[T](x : T) { 
    def doSomething = x 
} 

object Sugar { 
    type S[T] = { def doSomething : T } 
    def apply[T, X <: S[T]] (x: X) = x.doSomething 
} 

object Error { 
    val a = new Minimal(4) 
    Sugar(a) // error: inferred [Nothing, Minimal[Int]] does not fit the bounds of apply 
    Sugar[Int, Minimal[Int]](a) // works as expected 
} 

Le problème est que le compilateur parvient à comprendre le paramètre intérieur pour Minimal (Int), mais définit alors l'autre occurrence de T-Nothing, ce qui ne correspond pas à l'évidence apply. Ce sont certainement les mêmes T, en supprimant le premier paramètre, le second se plaigne que T n'est pas défini.

Y at-il une certaine ambiguïté qui signifie que le compilateur ne peut pas déduire le premier paramètre, ou est-ce un bug? Puis-je travailler autour de cela avec élégance?

Informations supplémentaires: Ce code est un exemple simple de tentative de sucre syntaxique. Le code original essaie de faire |(a)| signifie le module de a, où a est un vecteur. Clairement |(a)| est mieux que d'écrire |[Float,Vector3[Float]](a)|, mais malheureusement, je ne peux pas utiliser unary_| pour rendre cela plus facile.

L'erreur réelle:

inferred type arguments [Nothing,Minimal[Int]] do not conform to method apply's type parameter bounds [T,X <: Sugar.S[T]]

Répondre

9

Ce n'est pas un bogue du compilateur Scala, mais c'est certainement une limitation de l'inférence de type de Scala. Le compilateur veut déterminer la borne sur X, S[T], avant de résoudre pour X, mais la borne mentionne la variable de type sans contrainte jusqu'ici T qu'elle fixe donc à Nothing et procède à partir de là. Il ne revisite pas T une fois que X a été entièrement résolu ... actuellement l'inférence de type se poursuit toujours de gauche à droite dans ce genre de cas.

Si votre exemple représente avec précision votre situation réelle alors il y a une solution simple,

def apply[T](x : S[T]) = x.doSomething 

ici T sera déduit de telle sorte que Minimal est conforme à S[T] directement plutôt que par un intermédiaire variable de type borné.

Mise à jour

solution de Joshua évite aussi le problème du type déduisant T, mais d'une manière complètement différente.

def apply[T, X <% S[T]](x : X) = x.doSomething 

desugars à,

def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething 

Les variables de type TX et peuvent maintenant être résolus pour indépendamment (parce que T n'est plus lié mentionné dans l » X).Cela signifie que X est inféré comme Minimal immédiatement, et T est résolue dans le cadre de la recherche implicite d'une valeur de type X => S[T] pour satisfaire l'argument implicite conv. conforms dans scala.Predef fabrique des valeurs de cette forme, et dans le contexte garantira que, étant donné un argument de type Minimal, T sera déduit comme Int. Vous pouvez voir cela comme une instance de functional dependencies au travail dans Scala.

+0

Oui, cette solution fonctionne très bien dans mon cas. C'est aussi plus propre que la solution de Joshua (désolé Joshua!). Pouvez-vous expliquer pourquoi la solution de Joshua fonctionne, alors? – Dylan

+0

Réponse mise à jour pour expliquer pourquoi la solution de Joshua fonctionne également. –

4

Il y a quelques étrangetés avec des limites sur les types de structure, essayez d'utiliser une vue sur S lié [T] à la place.

def apply[T, X <% S[T]] (x: X) = x.doSomething fonctionne très bien.

+1

Très bien, cela fonctionne, mais il ne devrait sûrement pas y avoir de différence entre les approches? Prenant une vue dans ce cas est juste coulée à une super-classe, n'est-ce pas? Est-ce que cela signifie que cette correction est juste une solution de contournement pour un bogue dans le compilateur? – Dylan

+0

Ah, maintenant je vois, 'S' n'est pas une superclasse - c'est juste une vue (dans un sens). Ainsi, une vue liée est plus appropriée, même si elle n'est pas requise dans la plupart des cas. – Dylan

Questions connexes