views:

164

answers:

2

Hi

I'm having a little scala (version 2.8.0RC1) problem with implicit conversions. Whenever importing more than one implicit conversion the first one gets shadowed. Here is the code where the problem shows up:

// 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) })
  }
}

When uncommenting the second import statement everything goes wrong since the first "extendToMonad" is shadowed.

However, this one works:

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()
  }
}

So, where is the catch? What am I missing?

Regards, raichoo

A: 

Hello, my guess is that the compiler regards

implicit object IdentityMonad extends Monad[Value] 

as more specific than

implicit object MaybeMonad extends Monad[Maybe]

as part of the resolution process. There's an article posted by Daniel which covers this issue here. With your second example, the calls resolve an implicit directly by type and the above resolution rule is not required.

Don Mackenzie
+1  A: 

Indeed bindings and imported bindings are shadowed by name. This applies equally to imported implicit conversions.

I believe that you could rename during the import as a workaround:

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

You may like to look at Scalaz, in particular scalaz.{Functor, Scalaz, MA} for another way to encode these type classes. In particular, when searching for a typeclass Monad[X], the companion object Monad is searched.

retronym
The idea with renaming sounded good, but scala won't find both implicits after that.
raichoo