Calling generic function with Functor using subclass (cats/scalaz)

跟風遠走 提交于 2019-12-01 06:55:57

This isn't generally possible as Dmytro's answer points out. This is the reason that cats/scalaz exposes a .some extension method that is typed as returning Option, whereas using the Some constructor returns Some;

takesAFunctor(1.some)

Alternately you can use the more general Apply syntax; takesAFunctor(1.pure[Option])

is there some sort of "implicit widening" that I don't know about, or is this just an unfortunate drawback to functional programming in Scala using the stdlib?

The implicit widening you see when summoning the Option functor manually is covariance. The instance is defined invariantly for Option which is why Some isn't acceptable - the compiler can't find the implicit, but Functor[Option].map expects an Option or any subtype of Option, which is why Some works.

The drawback that you mention here is basically an impedance mismatch between the java-ish covariant subtyping and the more haskell-ish invariantly typed typeclasses

// No implicit for Functor[Some]. 
// Makes sense, but can we summon one since we have a Functor[Option]?

How would you define such instance in general case?

  implicit def subtypeFunctor[F[_], G[T] <: F[T]](implicit functor: Functor[F]): Functor[G] = new Functor[G] {
    override def map[A, B](ga: G[A])(f: A => B): G[B] = functor.map(ga)(f)
  }

doesn't work since functor.map(ga)(f) is generally of type F[B] and not necessarily G[B].

So generally no, it's impossible to derive functor for subtype constructor and reasons are fundamental.

Functor F maps objects T to objects F[T] and morphisms f: A => B to morphisms map(f): F[A] => F[B] (plus some laws). F in F[B] is in covariant position and F in F[A] is in contravariant position, so the only option for functor type class is to be invariant in type constructor.

By the way, you can also call takesAFunctor as takesAFunctor[Option](Some(1)).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!