Improvement in Scala 2.13 and sbt 1.3

There was Scala 2.13.1 release recently and sbt 1.3.0 was release in early September. sbt 1.3.1 was also released just about two weeks after sbt 1.3.0 release.

I’d like to talk about some improvement in Scala 2.13 (not just 2.13.1). Yet, before doing that, I’d like to quickly mention one thing that sbt 1.3.x has much better than the previous versions.

sbt 1.3.x

From sbt 1.3.0, Coursier is used as the default artifact fetcher instead of ivy. Coursier is written in Scala and known to be much faster than ivy. I haven’t benchmarked anything but I can feel that it’s faster.

Scala 2.13.x

Improvement (Partial Unification is enabled by default)

Updated: This is not really an improvement in Scala 2.13. It’s because partial unification is on by default in Scala 2.13. It’s the same as having scalacOptions += "-Ypartial-unification" in older Scala. In 2.13, -Ypartial-unification is no longer available as it’s always enabled by default.

Now for Scala 2.13, I don’t want to repeat what the release note or changelog says. I just want to share some improvement I experienced.

There’s an improvement in type inference for higher-kinded type with more than one type parameter.

The Scala versions prior to 2.13, the following code using Scalaz results in compile-time error.

import scalaz._, Scalaz._

for {
  a <- EitherT(Writer(List("Get a"), 1.right[String]))
  b <- EitherT(Writer(List("Get a + 99"), (a + 99).right[String]))
  c <- EitherT(Writer(List("a + b + 898"), (a + b + 898).right[String]))
} yield c
[error] /path/to/SomeApp.scala:18:12: no type parameters for method apply: (run: F[A \/ B])scalaz.EitherT[F,A,B] in object EitherT exist so that it can be applied to arguments (scalaz.WriterT[scalaz.Id.Id,List[String],String \/ Int])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : scalaz.WriterT[scalaz.Id.Id,List[String],String \/ Int]
[error]  required: ?F[?A \/ ?B]
[error]       a <- EitherT(Writer(List("Get a"), 1.right[String]))
[error]            ^
[error] /Users/kevinlee/git/kevin/github/github/test-project/src/main/scala/example/SomeApp.scala:18:26: type mismatch;
[error]  found   : scalaz.WriterT[scalaz.Id.Id,List[String],String \/ Int]
[error]  required: F[A \/ B]
[error]       a <- EitherT(Writer(List("Get a"), 1.right[String]))
[error]                          ^
[error] two errors found

Using Cats doesn’t help either.

import cats._, data._, implicits._

for {
  a <- EitherT(Writer(List("Get a"), 1.asRight[String]))
  b <- EitherT(Writer(List("Get a + 99"), (a + 99).asRight[String]))
  c <- EitherT(Writer(List("a + b + 898"), (a + b + 898).asRight[String]))
} yield c
[error] /path/to/SomeApp.scala:33:12: no type parameters for method apply: (value: F[Either[A,B]])cats.data.EitherT[F,A,B] in object EitherT exist so that it can be applied to arguments (cats.data.WriterT[cats.Id,List[String],Either[String,Int]])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : cats.data.WriterT[cats.Id,List[String],Either[String,Int]]
[error]  required: ?F[Either[?A,?B]]
[error]       a <- EitherT(Writer(List("Get a"), 1.asRight[String]))
[error]            ^
[error] /Users/kevinlee/git/kevin/github/github/test-project/src/main/scala/example/SomeApp.scala:33:26: type mismatch;
[error]  found   : cats.data.WriterT[cats.Id,List[String],Either[String,Int]]
[error]  required: F[Either[A,B]]
[error]       a <- EitherT(Writer(List("Get a"), 1.asRight[String]))
[error]                          ^
[error] two errors found

To make it work in Scala prior to 2.13, type parameters should be specified and for the Monad which EitherT takes, type alias should be used like this.

import scalaz._, Scalaz._

type MyWriter[A] = Writer[List, A]

for {
  a <- EitherT[MyWriter, String, Int](Writer(List("Get a"), 1.right[String]))
  b <- EitherT[MyWriter, String, Int](Writer(List("Get a + 99"), (a + 99).right[String]))
  c <- EitherT[MyWriter, String, Int](Writer(List("a + b + 898"), (a + b + 898).right[String]))
} yield c

How tedious! So much boilerplate code.

Now with Scala 2.13, the first two code snippets just work.

import scalaz._, Scalaz._

for {
  a <- EitherT(Writer(List("Get a"), 1.right[String]))
  b <- EitherT(Writer(List("Get a + 99"), (a + 99).right[String]))
  c <- EitherT(Writer(List("a + b + 898"), (a + b + 898).right[String]))
} yield c
EitherT(WriterT((List(Get a, Get a + 99, a + b + 898),\/-(999))))
import cats._, data._, implicits._

for {
  a <- EitherT(Writer(List("Get a"), 1.asRight[String]))
  b <- EitherT(Writer(List("Get a + 99"), (a + 99).asRight[String]))
  c <- EitherT(Writer(List("a + b + 898"), (a + b + 898).asRight[String]))
} yield c
EitherT(WriterT((List(Get a, Get a + 99, a + b + 898),Right(999))))

Comments