Excellent post by John A De Goes about the critique of tagless-final style of abstracting over program effects and program execution (control flow).
https://degoes.net/articles/zio-environment
In RChain source code tagless-final style is used (mostly) and knowing what are the limitations and where is the hidden complexity, can be very useful. Notes here should help in easier understanding of the points presented in the post.
There are two popular libraries for functional programming in Scala cats and scalaz. Because cats is used in RChain it's also in this examples.
First three programs are completely the same, just written with the different Scala syntax. It shows why for ... yield
(monadic) comprehension is useful to "transform" nesting to a sequence similar to imperative programs.
In program4
control effect we have is only Applicative so getStrLn
cannot give the result to the next instruction, data flow is static.
import cats.{Applicative, Monad}
import cats.syntax.all._
// Our effect interface / language
trait Console[F[_]] {
def putStrLn(line: String): F[Unit]
def getStrLn: F[String]
}
// Some Scala machinery to support use of `Console[F].<method>` in program
object Console {
def apply[F[_]](implicit instance: Console[F]): Console[F] = instance
}
// Different variants of "combined" program
def program1[F[_]: Monad: Console]: F[String] =
for {
_ <- Console[F].putStrLn("Good morning, what's your name?")
name <- Console[F].getStrLn
_ <- Console[F].putStrLn(s"Great to meet you, $name")
} yield name
def program2[F[_]: Monad: Console]: F[String] =
Console[F].putStrLn("Good morning, what's your name?").flatMap { _ =>
Console[F].getStrLn.flatMap { name =>
Console[F].putStrLn(s"Great to meet you, $name").flatMap { _ =>
name.pure[F]
}
}
}
def program3[F[_]: Monad: Console]: F[String] =
Console[F].putStrLn("Good morning, what's your name?") *>
Console[F].getStrLn.flatMap { name =>
// For the last step we can just use map
Console[F].putStrLn(s"Great to meet you, $name").map { _ =>
name
}
}
def program4[F[_]: Applicative: Console]: F[String] =
// We cannot use `flatMap` because it's defined on Monad
Console[F].putStrLn("Good morning, what's your name?") *>
// Result from `getStrLn` will be returned, as arrows are pointing
Console[F].getStrLn <*
// Results from both `putStrLn` are ignored
Console[F].putStrLn(s"Great to meet you")
Monad in cats - sequential execution flow.
https://typelevel.org/cats/typeclasses/monad.html
Effects in cats. Monad + suspend + concurrent execution flow.
https://typelevel.org/cats-effect/typeclasses/
Lambda World 2018 - A roadtrip with monads: from MTL, through tagless to BIO - Paweł Szulc
https://youtu.be/eth4y015BCU
Advanced Tagless Final - Saying farewell to Free (Luka Jacobowitz)
https://youtu.be/E9iRYNuTIYA