Tagless Final

Learn about the advantages of tagless final.

We'll cover the following


We could summarize tagless final by saying that it raises the level of abstraction of our program, specifically in regard to our use of monads. When we develop our DSL, our domain-specific mini-language, we often base it on monads. An example would be when we have to validate or do any kind of I/O. In previous chapters, we used the existing fp-ts monads like Either or Task directly as return types. In the tagless final, we’d instead declare a new abstract return type by convention called F. This F can’t be just about anything. It should definitely be a monad, and we might limit its type even further. On the other hand, it’s much less specific than using one particular monad.

Advantages of tagless final

The advantages we gain from this abstraction are two-fold. First, by using monads as a basic element in our applications, we can easily chain operations. In many of the other languages where the tagless final is popular (like Scala or Haskell) this is accomplished using do notation. Second, our code doesn’t depend on one specific monad. We can run it with whatever we want, as long as it fits the requirements we specified. In fact, we need a so-called interpreter for F to actually run the program we defined. An obvious advantage of this is that we can pull in a fake interpreter when we want to test our code, replacing IO with mocks, and so on.

Note: Do notation is also available within fp-ts, but it’s less fluent, and we might still prefer piping.

Interesting to note is that one of the people who made tagless final popular in the Scala world now seems to be advising against its use. He’s come to believe that it’s overkill for most projects and “premature indirection,” a level of abstraction that might sound good but won’t serve any practical use.

Get hands-on with 1200+ tech skills courses.