Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Scala support partial application of type constructors?

Tags:

I'm learning Cats from scala-exercises.
Wonder how to use high order type, there are some attempt with it:

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

 def someThingFail1[In]() = new Functor[Function1[In, _]] {
    override def map[A, B](fa: Function1[In, A])(f: A => B): Function1[In, B] = ???
  }

  def someThingFail2[In]() = new Functor[Either[In, _]] {
    override def map[A, B](fa: Either[In, A])(f: A => B): Either[In, B] = ???
  }

  def someThingFail3() = new Functor[List[_]] {
    override def map[A, B](fa: List[A])(f: A => B): List[B] = ???
  }

  //only this one can compile 
  def someThingRight1() = new Functor[List] {
    override def map[A, B](fa: List[A])(f: A => B): List[B] = ???
  }

ahead of three function can't compiled, error message like this:

[error] /Users/lorancechen/version_control_project/_tutorials/learn-cats/src/main/scala/mycats/Main.scala:16:42: Either[In, _] takes no type parameters, expected: one
[error]   def someThingFail2[In]() = new Functor[Either[In, _]] {
[error]                                          ^

Why Scala not support type hole? Will Dotty compiler support it? Thanks

like image 755
LoranceChen Avatar asked Feb 23 '18 14:02

LoranceChen


1 Answers

That's because in Scala 2.x

Either[X, _]

is an existential type, it can be written out explicitly as

Either[X, Y] forSome { type Y }

This is somewhat similar to Java's wildcards, and not what you want as an argument for a higher order Functor type constructor. What you want is a type lambda. Originally, this could be written down as follows:

({type lam[Y] = Either[X, Y]})#lam

The fact that type lambdas could be used in Scala at all was not a planned feature, but rather an accidental discovery, and the syntax was somewhat lengthy. However, there is the non/kind-projector plugin that simplifies it dramatically. With this plugin, you code becomes:

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

def someThingFail1[In]() = new Functor[Function1[In, ?]] {
  override def map[A, B](fa: Function1[In, A])(f: A => B): Function1[In, B] = ???
}

def someThingFail2[In]() = new Functor[Either[In, ?]] {
  override def map[A, B](fa: Either[In, A])(f: A => B): Either[In, B] = ???
}

def someThingFail3() = new Functor[List[?]] {
  override def map[A, B](fa: List[A])(f: A => B): List[B] = ???
}

//only this one can compile 
def someThingRight1() = new Functor[List] {
  override def map[A, B](fa: List[A])(f: A => B): List[B] = ???
}

(Tested with scala 2.12.4, cats 1.0.1, kind-projector 0.9.4).

Note that this plugin is used in cats source code (search for ?).

Everything will become much better in dotty, it already supports a neat syntax for type lambdas.

like image 156
Andrey Tyukin Avatar answered Sep 23 '22 13:09

Andrey Tyukin