Can we assign Functor of Some to Functor of Option


#1

Here is the functor definition

  trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}
 

Question is:

    val optFunctor = new Functor[Option] {...}
    val someFunctor = new Functor[Some] {...}

    // can I do this ? If not why ?
   val anotherSomeFunctor:Functor[Option] = someFunctor

   whereas the following works

   val x:Seq[Int] = ???
    val y:List[Int] = ???
    val z:Seq[Int] = y

#2

If you try to compile this code, you will get a type mismatch error from the compiler, and it should also tell you

Note: Some <: Option, but trait Functor is invariant in type F.
You may wish to define F as +F instead. (SLS 4.5)

Your Functor trait being invariant in F means, that a Functor[A] and a Functor[B] do not have any subtyping relation, regardless of any subtype relation between A and B.

The compiler suggests the fix of defining F as +F, which means making Functor covariant in F, so Functor[A] is a subtype of Functor[B] if A is a subtype of B:

trait Functor[+F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

But this will also cause a compile error:

covariant type F occurs in contravariant position in type F[A] of value fa

The reason is, that a covariant type may only be used in a covariant position, i.e as a return type (usually), but not as a parameter to a method (a contravariant position).

This rule is necessary, because you could have invalid code otherwise. A valid implementation for Functor[Some] could look like this:

new Functor[Some] {
  def map[A,B](fa: Some[A])(f: A=>B): Some[B] = Some(f(fa.value))
}

If you could assign this to a Functor[Option] value, the method on it would allow passing Options, which do not have the value field (they may be None after all).

Regarding your example with Seq and List, you can see that Seq[+A] is covariant in its type parameter. You may wonder how this is possible, as the documentation contains methods like this:

def +:(elem: A): Seq[A]

But the documentation is actually cheating here to make the signature look simpler. If you look at the source code, the real signature is

def +: [B >: A](elem: B): CC[B]

so it takes any B, which is a supertype of A, and creates a collection of type B.

You will also note, that Seq does not contain any methods that mutate the collection. All mutable collections in the standard library are invariant in their element type, as their methods for adding need to take elements of the generic type.

More on variance can be found in the Scala Tour.


#3

Thank You. It all makes sense now.