partial polymorphic type inference

35 views
Skip to first unread message

Normen Müller

unread,
May 22, 2012, 9:36:52 AM5/22/12
to sca...@googlegroups.com
Hi,
 
setting up some initial steps for Scalaz beginners (w/o using Scalaz in the first step), I hit on the following:
 
trait Functor[F[_]] { def fmap[A,B](f: A => B): F[A] => F[B] }
 
object Functor {
  def fmap[A,B, F[_]](fa: F[A])(f: A => B)(implicit functor: Functor[F]): F[B] =
    functor.fmap(f)(fa)
 
  implicit object ListFunctor extends Functor[List] {
    override def fmap[A,B](f: A => B): List[A] => List[B] = _ map f
  } 
  implicit object OptionFunctor extends Functor[Option] {
    override def fmap[A,B](f: A => B): Option[A] => Option[B] = _ map f
  } 
  implicit object Functio0Functor extends Functor[Function0] {
    override def fmap[A,B](f: A => B): Function0[A] => Function0[B] = a => () => f(a())
  }
  implicit def Function1Functor[D]: Functor[({type λ[α]=Function1[D,α]})#λ] =
    new Functor[({type λ[α]=Function1[D,α]})#λ] {
      override def fmap[A,B](f: A => B) = (g: D => A) => g andThen f
    }
}
object Main {
  def main(args: Array[String]): Unit = {
    import Functor._
   
    def some[A](a: A): Option[A] = Some(a)
    def none[A]: Option[A] = None
   
    assert(fmap(List(1, 2, 3))(1 +) == List(2, 3, 4))
    assert(fmap(some(1))(1 +) == some(2))
   
    val f = (s: String) => s.length
    val g = (i: Int) => i % 2 == 0
   
    val lf0 = fmap(() => "abc")(f)
    assert(lf0() == 3)   
   
    val lf1 = fmap[Int, Boolean, ({type λ[α]=Function1[String,α]})#λ](f)(g)
    assert(lf1("abc") == false)
   
    val `lf1'` = fmap(f)(g) // <<< doesn't compile !!!
  }
}
 
Why is explicit typing required for lf1'?
 
Cheers,
  /nm

Jason Zaugg

unread,
May 22, 2012, 10:35:48 AM5/22/12
to sca...@googlegroups.com
Partially applied type constructors are never inferred. It's not an
easy task in the face of subtyping:

https://issues.scala-lang.org/browse/SI-2712

Miles Sabin discovered a neat way to use dependent method types to
approximate this feature: see the comments on that ticket, and
scalaz.Unapply in the scalaz-seven branch.

-jason

Normen Müller

unread,
May 24, 2012, 1:42:15 AM5/24/12
to sca...@googlegroups.com
Hi Jason,
 
Thanks for your reply! Got it.
 
But why don't we need that explicit typing using Scalaz (ExampleFunctor):
 

// Map across the Function1 functor

val len = (s: String) => s.length

(len ∘ (1 + (_: Int))).apply("foo") assert_=== 4

Cheers,
  /nm

Jason Zaugg

unread,
May 24, 2012, 1:54:58 AM5/24/12
to sca...@googlegroups.com
On Thu, May 24, 2012 at 7:42 AM, Normen Müller
<normen....@googlemail.com> wrote:

> Hi Jason,
>
> Thanks for your reply! Got it.
>
> But why don't we need that explicit typing using Scalaz (ExampleFunctor):
>
>
> // Map across the Function1 functor
>
> val len = (s: String) => s.length
>
> (len ∘ (1 + (_: Int))).apply("foo") assert_=== 4
>
> Cheers,
>   /nm

We first implicitly convert (I => O) to MA[[a](I => a), O]*. We wrote
a bunch conversions like this for various binary (and higher arity)
type constructors, e.g. Function2, Tuple3. (A single implicit suffices
for type constructor like List/Option, as the compiler can infer M.)

Inside MA, we define the method ∘, which can access the type
constructor M without further inference.

scalaz-seven uses Unapply, which means we only have to write a
conversion for each kind, rather than for each type based on a
non-unary type constructor. This was especially important, as we no
longer put all these methods in the monolithic MA, but rather spread
them into FunctorOps / MonadOps etc.

-jason

Normen Müller

unread,
May 24, 2012, 2:03:08 AM5/24/12
to sca...@googlegroups.com
Hi,
 
OK.
 
I am still using Scalaz6... but I think it's time to have a look into Scalaz7. Is there any documentation on the new design and their motivation?
 
  /nm
 

-jason

Jason Zaugg

unread,
May 24, 2012, 2:12:25 AM5/24/12
to sca...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages