2010-04-25 7 views
3

Je rencontre un petit problème avec les conversions implicites (version 2.8.0RC1). Chaque fois que vous importez plus d'une conversion implicite, la première est ombrée. Voici le code où le problème apparaît:Surcharge des conversions implicites génériques

// containers 
class Maybe[T] 
case class Nothing[T]() extends Maybe[T] 
case class Just[T](value: T) extends Maybe[T] 

case class Value[T](value: T) 

trait Monad[C[_]] { 
    def >>=[A, B](a: C[A], f: A => C[B]): C[B] 
    def pure[A](a: A): C[A] 
} 

// implicit converter 
trait Extender[C[_]] { 
    class Wrapper[A](c: C[A]) { 
    def >>=[B](f: A => C[B])(implicit m: Monad[C]): C[B] = { 
     m >>= (c, f) 
    } 

    def >>[B](b: C[B])(implicit m: Monad[C]): C[B] = { 
     m >>= (c, { (x: A) => b }) 
    } 
    } 

    implicit def extendToMonad[A](c: C[A]) = new Wrapper[A](c) 
} 

// instance maybe 
object maybemonad extends Extender[Maybe] { 
    implicit object MaybeMonad extends Monad[Maybe] { 
    override def >>=[A, B](a: Maybe[A], f: A => Maybe[B]): Maybe[B] = { 
     a match { 
     case Just(x) => f(x) 
     case Nothing() => Nothing() 
     } 
    } 

    override def pure[A](a: A): Maybe[A] = Just(a) 
    } 
} 

// instance value 
object identitymonad extends Extender[Value] { 
    implicit object IdentityMonad extends Monad[Value] { 
    override def >>=[A, B](a: Value[A], f: A => Value[B]): Value[B] = { 
     a match { 
     case Value(x) => f(x) 
     } 
    } 

    override def pure[A](a: A): Value[A] = Value(a) 
    } 
} 

import maybemonad._ 
//import identitymonad._ 

object Main { 
    def main(args: Array[String]): Unit = { 
    println(Just(1) >>= { (x: Int) => MaybeMonad.pure(x) }) 
    } 
} 

Lorsque décommentant la seconde tout déclaration d'importation va mal depuis le premier « extendToMonad » est occulté.

Cependant, celui-ci fonctionne:

object Main { 
    implicit def foo(a: Int) = new { 
    def foobar(): Unit = { 
     println("Foobar") 
    } 
    } 

    implicit def foo(a: String) = new { 
    def foobar(): Unit = { 
     println(a) 
    } 
    } 

    def main(args: Array[String]): Unit = { 
    1 foobar() 
    "bla" foobar() 
    } 
} 

Alors, où est le piège? Qu'est-ce que je rate?

Cordialement, raichoo

+3

Une note de côté: Je pense que «Nothing» devrait être un objet qui étend 'Maybe'. Aussi, vous devriez envisager de choisir un nom différent pour cet objet car Nothing est déjà défini comme le sous-type de tous les types. – missingfaktor

+1

Qu'en est-il de l'objet case? None extends Maybe [Nothing] '? «None» est déjà utilisé dans la bibliothèque standard (exactement dans le même but). – missingfaktor

+0

Je dois préciser que ce code ne sera jamais mis en production. C'est juste un test pour moi-même pour comprendre quelques concepts de scala;) – raichoo

Répondre

0

je suppose que le compilateur considère

implicit object IdentityMonad extends Monad[Value] 

comme plus précis que

implicit object MaybeMonad extends Monad[Maybe] 

dans le cadre du processus de résolution. Il y a un article posté par Daniel qui couvre ce numéro here. Avec votre deuxième exemple, les appels résolvent implicitement directement par type et la règle de résolution ci-dessus n'est pas requise.

1

En effet, les liaisons et les liaisons importées sont masquées par leur nom. Cela s'applique également aux conversions implicites importées.

Je crois que vous pouvez renommer lors de l'importation comme solution de contournement:

import IdentityMonad.{extendToMonad => extendToMonadIdentity} 
import MaybeMonad.{extendToMonad => extendToMonadMaybe} 

Vous pouvez vous regarder Scalaz, en particulier scalaz.{Functor, Scalaz, MA} pour une autre façon de coder ces classes de type. En particulier, lors de la recherche d'une classe de type Monad[X], l'objet compagnon Monad est recherché.

+0

L'idée de renommer sonnait bien, mais scala ne trouvera pas les deux implicits après cela. – raichoo

Questions connexes