scalafunctional-programmingtagless-final

Typeclass dependency with tagless-final


After watching John De Goes' "FP to the Max" (https://www.youtube.com/watch?v=sxudIMiOo68) I'm wondering about approaches for writing FP programs in the tagless-final pattern.

Say I have some typeclass for modelling a side-effecty thing (taking his Console example):

trait Console[F[_]] {
  def putStrLn(str: String): F[Unit]
  def getStrLn: F[String]
}

How would you depend on Console?

Implicitly

Like demonstrated in his video:

def inputLength[F[_]: Functor: Console]: F[Int] =
  Console[F].getStrLn.map(_.length)

Pros: The function signature is clean, and you can benefit from typeclass automatic derivation

Explicitly

By passing the instance to the function directly:

def inputLength[F[_]: Functor](console: Console[F]): F[Int] =
  console.getStrLn.map(_.length)

Pros: This allows you to explicitly wire your dependencies according to your needs, and feels less "magical"

Not sure what's the best / most idiomatic way to write this function, would appreciate your opinions.

Thanks!


Solution

  • When you rely on a typeclass instance via implicit parameters, there's one thing you're certain of, and that is that you can determine the instance of your typeclass at compile time (unless you provide it explicitly, which kind of defeats the purpose and then we go back to example 2). On the contrary, if you can't determine the instance of that class at compile time, for example when you rely on a configuration parameter to determine the instance type, then an implicit parameter of any kind is no longer suitable.

    Thus, I'd say, to my own opinion and liking, is that whenever one can determine the instance at compile time and let the compiler figure out the wiring, do so, because as you said you gain a lot from it, such as the ability to enjoy automatic type class derivation when available.

    The argument of "magical", while understandable, signals that whomever says this still has milage to go in the language that he's programming in and needs to learn how things work, which is completely OK, yet not a good enough reason not to use typeclass instances via implicit parameters.