2010-12-06 5 views
7

Dans scala, nous pouvons utiliser impleclasses pour ajouter de manière conditionnelle des méthodes sur un type paramétré en fonction des paramètres de ce type. Par exemple, Iterator.sum:Ajout d'un contrôle de validité dépendant d'une classe de type (Implicits facultatifs)

def sum[B >: A](implicit num: Numeric[B]): B = foldLeft(num.zero)(num.plus) 

Il doit y avoir une instance des Numeric pour cette méthode classe de types de même être appelé:

scala> List(1, 2, 3).sum 
res0: Int = 6 

scala> List("a", "b").sum 
<console>:6: error: could not find implicit value for parameter num: Numeric[java.lang.String] 
     List("a", "b").sum 
       ^

Jusqu'à présent, si bon. Disons que je veux avoir un certain type de collection, My2Col:

class My2Col[A](a1 : A, a2 : A) 

Mais je veux exiger que, si cela est fait avec un A : Numeric, puisa2 > a1. Cependant, il est entièrement valide pour qu'il soit fait avec un A qui n'est pas numérique.

My2Col("a", "b") //OK 
My2Col("b", "a") //OK 
My2Col(1, 2)  //OK 
My2Col(2, 1)  //THROW IllegalArgumentException 

Quelqu'un at-il des idées quant à la façon dont je pourrais le faire?

PS. Si quelqu'un a des suggestions pour un meilleur titre de la question, je suis toutes les oreilles

Répondre

12
class My2Col[A](a1 : A, a2 : A)(implicit num: Numeric[A] = null){ 
    for{check <- Option(num); if(check.gteq(a1, a2))} 
    throw new IllegalArgumentException 
} 
+0

+1. Utilisation intelligente des valeurs d'arguments par défaut! – pedrofurla

+0

Nice, j'aime cette solution. – axel22

+0

@retronym est venu avec la même suggestion sur la liste de diffusion scala –

3

je voudrais mettre en œuvre en créant 2 implicits qui représentent les exigences, l'une étant plus générale (pour tous les types autres que, par exemple, Int ou Numeric[T]) et l'autre étant plus spécifique (pour Int ou Numeric[T]). Puis, en suivant les règles de résolution implicite, je mettrais le plus spécifique dans l'objet compagnon du type Requirement, et le plus général dans la classe de base de l'objet compagnon. De cette façon, je m'assurerais que le compilateur essaie d'appliquer le plus spécifique en premier. L'inconvénient est évidemment que si l'utilisateur fournissait explicitement le paramètre implicite, ce mécanisme pourrait être contourné pour ne pas effectuer le contrôle.

Quelque chose comme ça (où Int est le type avec des règles spécifiques et Foo est la classe de collection):

package ex                           

    trait Requirement[T] {                        
    def check(a1: T, a2: T): Unit                     
    }                             

    trait BaseReq {                         
    implicit def genericReq[T] = new Requirement[T] {                
     def check(a1: T, a2: T) {println("generic")}                 
    }                            
    }                             

    object Requirement extends BaseReq {                    
    implicit object IntReq extends Requirement[Int] {                
     def check(a1: Int, a2: Int) = {                    
     println("int")                        
     if (a2 <= a1) throw new IllegalArgumentException                
     }                            
    }                            
    }                             

    class Foo[T](a1: T, a2: T)(implicit req: Requirement[T]) {               
    req.check(a1, a2)                        

    // whatever `foo` does follows                     
    }                            

    object Main {                          
    def main(args: Array[String]) {                     
     new Foo(1, 2)                         
     new Foo("S1", "S2")                       
     new Foo(2, 1)                         
    }                            
    } 
+0

Je me demande comment faire Foo [T: Exigence]. Une idée? – pedrofurla

+0

Hm, je ne suis pas sûr si on peut alors accéder à l'objet 'Requirement' pour invoquer ses méthodes .. – axel22

+1

@ axel22 Si vous utilisez un contexte lié (ie' Foo [T: Requirement] '), vous pouvez utiliser implicitement [Exigence [T]] 'pour se référer à l'exigence' T ['' '' '' '' '' '' '' ''

Questions connexes