Scala: Type inference and subtypes/higher-kinded-types

952 views Asked by At

I've been playing around with Scalaz to get a little bit of the haskell feeling into scala. To understand how things work in scala I started implementing various algebraic structures myself and came across a behavior that has been mentioned by the Scalaz folks.

Here is my example code which implements a functor:

trait Functor[M[_]] {
  def fmap[A, B](a: M[A], b: A => B): M[B]
}

sealed abstract class Foo[+A]
case class Bar[A]() extends Foo[A]
case class Baz[A]() extends Foo[A]

object Functor {

  implicit val optionFunctor: Functor[Option] = new Functor[Option]{
    def fmap[A, B](a: Option[A], b: A => B): Option[B] = a match {
      case Some(x) => Some(b(x))
      case None => None
    }   
  }

  implicit val fooFunctor: Functor[Foo] = new Functor[Foo] {
    def fmap[A, B](a: Foo[A], b: A => B): Foo[B] = a match {
      case Bar() => Bar()
      case Baz() => Baz()
    }   
  }
}

object Main {
  import Functor._

  def some[A](a: A): Option[A] = Some(a)
  def none[A]: Option[A] = None

    def fmap[M[_], A, B](a: M[A])(b: A => B)(implicit f: Functor[M]): M[B] =
      f.fmap(a, b)

  def main(args: Array[String]): Unit = { 
    println(fmap (some(1))(_ + 1)) 
    println(fmap (none)((_: Int) + 1)) 
    println(fmap (Bar(): Foo[Int])((_: Int) + 1))                                                    
  }
}

I wrote a functor instance for Option and a bogus sumtype Foo. The problem is that scala cannot infer the implicit parameter without an explicit type annotation or a wrapper method

def some[A](a: A): Option[A] = Some(a)

println(fmap (Bar(): Foo[Int])((_: Int) + 1))

Scala infers types like Functor[Bar] and Functor[Some] without those workarounds.

Why is that? Could anyone please point me to the section in the language spec that defines this behavior?

Regards, raichoo

1

There are 1 answers

0
retronym On BEST ANSWER

You're asking the compiler to perform two tasks: local type inference (§6.26.4) of the type arguments to fmap, and an implicit search for implicit parameter (§7.2) f. References are to the Scala Reference.

Things go in roughly this order:

fmap[M = ?, A = ?, B = ?](Some(1))(x => x)

// type the arguments of the first parameter section. This is done
// without an expected type, as `M` and `A` are undetermined.
fmap[M = ?, A = ?, B = ?](Some(1): Some[Int])(x => x)(?) 

// local type inference determines `M` and `A`
fmap[Some, Int, B = ?](Some(1): Some[Int])(x => x)(?) 

// move to the second parameter section, type the argument with the expected type
// `Function1[Int, ?]`. The argument has the type `Function1[Int, Int]`
fmap[Some, Int, ?](Some(1): Some[Int])((x: Int) => x)                                                 

// local type inference determines that B = Int
fmap[Some, Int, Int](Some(1): Some[Int])((x: Int) => x)

// search local identifiers, and failing that the implicit scope of type `Functor[Some]]`, for an implicit
// value that conforms to `Functor[Some]`
fmap[Some, Int, Int](Some(1): Some[Int])((x: Int) => x)(implicitly[Functor[Some]])

The implicit search for Functor[Some] fails; Functor[Option] doesn't conform.