2010-12-14 4 views
4

Dire que, pour des raisons esthétiques, je veux être capable d'écrire:opérateurs arithmétiques prépondérants sur Int via des conversions implicites

3/4 

et ont/une méthode sur une classe qu'il existe une conversion implicite de Int Cela est-il possible, par exemple: est-il possible de "désactiver" la méthode/sur Int?

+1

Pas assez d'un expert Scala pour vous dire si vous pouvez le faire, mais je peux vous dire que ce ne devrait pas faire cela. Sauf si vous essayez de construire "Scala Puzzlers" ou quelque chose, vous achetez juste beaucoup d'ennuis à un escompte abrupt. – Malvolio

+0

Pour donner un peu de contexte, ce que j'essaye de faire est d'écrire un comparateur d'URL où vous pouvez spécifier des modèles d'URL sous la forme de p. "foo/bar"/34/"quelque chose/chose". Évidemment, je pourrais vous \ ou toute autre chose, mais "foo/bar" \ 34 \ "quelque chose /" semble un peu bizarre. Je pense que l'utilisation de/devrait être très claire pour tout lecteur du code dans ce contexte. –

+0

Notez que dans votre exemple de commentaire, la conversion implicite serait String to Foo, pas Int –

Répondre

7

En bref: Non, vous ne pouvez pas.

La résolution implicite n'aura lieu que si vous essayez d'appeler une méthode qui n'existe pas déjà.

Une solution plus "idiomatiques" serait de créer votre propre type pseudo-nombre, quelque chose comme:

case class Rational(a: Int, b: Int) { 
    // other methods 
} 

val foo = Rational(3, 4) 

ou

case class Path(value: String) { 
    def /(other: String): Path = ... 
} 

val p = Path("3")/"4" 
+0

Je veux littéralement écrire 3/4, mais la signification de/n'est pas destinée à être une division numérique, mais plutôt une division de chemin. (Voir le commentaire à la question.) –

+0

Ensuite, créez une nouvelle classe en prenant le paramètre 3 comme constructeur, et définissez/sur * que * –

+0

Pourrait aussi faire juste "3"/"4". Encore une fois, une question d'esthétique. Le point est de faire un DSL aussi propre que possible avec un minimum d'encombrement. Le plus simple est alors probablement par ex. "foo"/n (3)/n (4) etc. avec une conversion implicite de la classe de cas n (i: Int) en une classe d'items générale. Ça me semble encore moche. Là encore, spécifier des entiers littéraux dans un modèle d'URL peut s'avérer tout à fait inutile en pratique (c'est-à-dire pourquoi ne pas simplement utiliser "foo/bar/3/4/some/thing"), donc la question peut avoir un intérêt académique. :) –

2

Y at-il une raison que quelque chose comme

trait PathElement[T] { val value: T } 
case class IntElement(value: Int) extends PathElement[Int] 
case class StringElement(value: String) extends PathElement[String] 

case class Path(parts: Seq[PathElement[_]]) { 
    def /(other: Path): Path = copy(parts = parts ++ other.parts) 
} 
object Path { 
    def apply(part: PathElement[_]): Path = Path(List(part)) 
    implicit def int2path(i: Int): Path = Path(IntElement(i)) 
    implicit def str2path(s: String): Path = Path(StringElement(s)) 
} 

ne fonctionnerait pas pour vous? Cela vous permettra d'écrire, par exemple,

import Path._ 
"foo"/3/4/"bar" 

Cela fonctionne parce que chaîne ne dispose pas de sa propre méthode /, de sorte que le premier "foo" est converti implicitement à un Path. Si vous démarrez un Path avec un Int, vous devrez le convertir explicitement, mais vous obtiendrez n'importe quel autre Int s gratuitement.

+0

C'est un excellent point que je n'ai pas vraiment considéré, que le problème ne se pose que lorsque l'Int est le premier dans la chaîne. Résout le problème pour la plupart des objectifs pratiques. (Etant donné qu'il y en a.) :) –

1

Je ne peux bien sûr que deviner ce que vous voulez vraiment accomplir, mais je suppose que vous ne voulez pas seulement faire correspondre des URL concrètes mais aussi extraire des informations de chaînes données. Par exemple. Quand on vous donne "/foo/21" vous ne voulez pas simplement savoir que cela correspond à "foo"/21 mais vous voulez faire quelque chose avec la valeur de 21.

J'ai trouvé le processus de correspondance URI dans Lift pour être très utile, alors peut-être que correspond à votre cas d'utilisation. (J'utilise une version très simplifiée, bien sûr.) C'est fait avec les listes là-bas ce qui rend la correspondance un peu plus facile mais cela signifie aussi que vous devrez utiliser :: au lieu de /.

Mais ce n'est pas le point: ce que je veux montrer est l'avantage de pas en utilisant les conversions implicites et la puissance des extracteurs

object AsInt { 
def unapply(i: String): Option[Int] = try { 
    Some(i.toInt) 
    } catch { 
    case e: java.lang.NumberFormatException => None 
    } 
} 

def matchUrl(url: String) = { 
    val req:List[String] = url.split('/').toList.drop(1) 
    req match { 
    case "foo" :: "bar" :: Nil => println("bar") 
    case "foo" :: AsInt(i) :: Nil => println("The square is " + i*i) 
    case "foo" :: s :: Nil => println("No int") 
    case _ => println("fail") 
    } 
} 

matchUrl("/foo/21") 
matchUrl("/foo/b") 
matchUrl("/foo/bar") 
matchUrl("/foobar") 

// prints: 
// The square is 441 
// No int 
// bar 
// fail 

En bref, en utilisant le lieu extracteur AsInt d'une conversion implicite de Int à String vous pouvez réellement récupérer la valeur entière de la chaîne si et seulement si elle est convertible et bien sûr l'utiliser immédiatement. Évidemment, si vous n'aimez pas le nommage, vous pouvez le changer pour quelque chose de plus discret, mais si vous voulez vraiment faire correspondre l'url, vous ne devriez peut-être pas tout convertir implicitement.

+0

En fait, ce n'est pas vraiment ce que j'essaie de faire, c'est plutôt de spécifier le modèle d'URL de la façon la plus simple possible, à la Scalatra mais avec des éléments typés. Maintenant, c'est relativement trivial à réaliser, je viens juste de me retrouver dans la partie "/ method on Int". –

Questions connexes