Example is taken from Tagless Final in Mastering Functional Programming:
trait Capabilities[F[_]] {
def resource(name: String): F[String]
def notify(target: String, text: String): F[Unit]
}
import cats.Monad
def income[F[_]](implicit M: Monad[F], C: Capabilities[F]): F[Unit] =
for {
contents <- C.resource("sales.csv")
total = contents
.split("\n").toList.tail // Collection of lines, drop the CSV header
.map { _.split(",").toList match // List[Double] - prices of each of the entries
{ case name :: price :: Nil => price.toDouble }
}
.sum
_ <- C.notify("admin@shop.com", s"Total income made today: $total")
} yield ()
In order to compile is, I must include:
import cats.implicits._
Without this, I get an error:
Error:(21, 27) value map is not a member of type parameter F[String] contents <- C.resource("sales.csv")
Two questions:
F
is not known in advance. But there are Monad
and Capabilities
define implicitly
for F
. Why Scala compiler cannot identify it without implicits import from the cats.cats
. For instance, import only cats.instances.list._
, to be more precise. What exactly is used by Scala compiler from cats.implicits._
, in order to be able to compile this code? And more important, what algorithm you use, to find it?-Xprint-args
option, code is compiled successfully even without cats.implicits._
importing. Can you please explain, how it affects it?What Scala compiler cannot identify without implicits import are just syntaxes. Instances are resolved properly without imports (because of implicit parameters).
Out of cats.implicits._
you actually use only
import cats.syntax.functor._
import cats.syntax.flatMap._
https://github.com/scala/scala/pull/5909
The option behaves like
-Xshow-phases
and stops the compiler after printing.
Without syntaxes imported you can write
def income[F[_]](implicit M: Monad[F], C: Capabilities[F]): F[Unit] =
M.flatMap(C.resource("sales.csv"))(contents => {
val total = contents
.split("\n").toList.tail // Collection of lines, drop the CSV header
.map {
_.split(",").toList match // List[Double] - prices of each of the entries
{ case name :: price :: Nil => price.toDouble }
}
.sum
M.map(C.notify("admin@shop.com", s"Total income made today: $total"))(_ => ())
})