ToFu: a Scala functional programming toolkit split into thirty modules
Functional programming toolbox
At a glance
- What is it?
- ToFu is a functional programming library for Scala aimed at reducing the friction of the tagless final pattern, and it is published as around thirty separate modules so that a consumer depends only on what it uses. The core utilities are duplicated per cats-effect line, and the interop modules are where the design intent shows.
- Who is it for?
- ToFu suits a Scala team that has committed to the tagless final style and wants to stop pulling in a whole effect-system stack for every utility, since the module split means the dependency graph matches what a project actually uses and the interop modules let the library sit alongside Cats, fs2 or ZIO rather than replacing them.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly Scala, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A toolkit for tagless final, published as thirty separate modules
ToFu is described in a single phrase as a functional programming toolkit aimed at taming the complexity of the Tagless Final approach, and the project description says the same thing more briefly, a functional programming toolbox.
The design consequence is visible in how the library is packaged. Instead of one artifact, the whole utils pack is a set of modules you pick from, and the whole set is added to a build.sbt like this:
libraryDependencies += "tf.tofu" %% "tofu" % "latest version in badge"Or you can specify an exact list of modules in place of the umbrella, and the list is long enough to be worth reading because it is a map of the library's concerns.
There is tofu-kernel for core independent utils, and inside it tofu-kernel-cats-mtl for interop between the kernel and Cats MTL. The core utils come as tofu-core-something with the suffix replaced by ce2 or ce3, depending on which cats-effect version you use. Then tofu-core-higher-kind, tofu-concurrent, tofu-config, tofu-data, tofu-derivation for derivation utils and derevo annotations, tofu-doobie, tofu-enums for Enumeratum, and tofu-env, which is an Env, a variation of a Reader Monad based on the Monix Task.
The interop modules are the interesting part of the list. There is one for fs2, one for ZIO split into core and logging instances, and a whole logging family: tofu-logging covers derivation, layout, structured, util and interop as a whole, with sub-modules for derivation only, layout only, structured only and util only, plus refined, shapeless and Log4Cats interop.
Then there is tofu-memo for caching, tofu-observable for the Monix reactive Observable, and tofu-streams.
That a library for the Scala effect ecosystem splits into thirty artifacts is not accidental. It is the answer to the problem the tagless final pattern creates, which is that a typeclass-based design forces every consumer to depend on every instance, and the fix is to make the dependency graph match the graph of what you actually use.
The project is Apache-2.0 licensed, written in Scala, published on Maven Central under the tf.tofu organisation, with the homepage at tofu-tf.github.io/tofu. The recent releases are v0.14.1, v0.14.2 in June 2026 and v0.14.3 in August 2026, and the last push was on 2026-10-18.
Two cats-effect lines means two copies of the core
The ce2 and ce3 suffix is the single most consequential decision in the module list, and it is easy to skim past.
The instruction is that the core utils come as tofu-core-something, and you replace the suffix with ce2 or ce3 depending on which cats-effect version you use. So a project on cats-effect 2 and a project on cats-effect 3 depend on different artifacts even when they want the same utility.
That is the consequence of the effect system being part of the type signatures. A utility that returns an effect has the effect type in its signature, and a version of the utility built against cats-effect 2 is not usable by a project on cats-effect 3 because the types do not line up. The alternative would be one artifact with a version that supports both, which means the library sits on a specific cats-effect line and forces its users onto it.
The examples directory follows the same split, with separate directories for examples/ce2/, examples/ce3/ and examples/zio2/. So there is a third example set for the ZIO interop path, which suggests the ZIO module is not merely a set of instances but a distinct way of using the library.
The interop modules generally are what make this practical. The kernel interop with Cats MTL is listed separately from the core, which is the pattern of a library that wants to work with whatever effect system you already have rather than insisting on its own. The fs2 interop, the ZIO core and logging instances, and the three logging interop modules for Refined, Shapeless and Log4Cats all follow the same reasoning.
The general principle is worth naming because it is the whole architecture of the project. Each of these is a separate artifact rather than a flag or a type class parameter, so a consumer pulls in only the bridge they need, and the version graph stays honest about what depends on what.
The microsite is built in a container, and the docs mount over it
The documentation site is a Docusaurus instance built and served from Docker, and the compose file shows a setup that is worth reading because it separates the container from the content.
version: "3.9"
services:
docusaurus:
build: .
ports:
- 3000:3000
- 35729:35729
volumes:
- ./docs:/app/docs
- ./website/blog:/app/website/blog
- ./website/i18n:/app/website/i18n
- ./website/src:/app/website/src
- ./website/static:/app/website/static
- ./website/sidebars.json:/app/website/sidebars.json
- ./website/docusaurus.config.js:/app/website/docusaurus.config.js
working_dir: /app/websiteThe service is named docusaurus, it builds from the repository root, and it publishes two ports, 3000 for the site and 35729 for the development server. The working directory inside the container is /app/website.
What follows is the interesting part. The volumes mount the docs directory, the website blog, the website i18n directory, the website src, the website static directory, the sidebars file and the docusaurus config, each as a separate mount into the container. That means the documentation content and the site configuration live on the host and are read by the container at run time, so you can edit a page and reload the site without rebuilding the image.
The Dockerfile is minimal and consistent with that. It starts from a Node long-term-support slim image on buster, sets the working directory to the app website directory, exposes the same two ports, copies the docs and website directories in, runs a yarn install and runs yarn start.
So the container is a development and preview environment rather than a production image, and the point of the compose file is to let a contributor run the site locally against the real content.
The top level of the repository supports that reading. There is a website/ directory for the site itself, a docs/ directory for the documentation content, a Dockerfile, a docker-compose.yml and a .dockerignore, alongside the code in modules/, the examples/ directory, the project/ directory for the sbt build definition, a build.sbt and a separate ci-release.sbt for the release pipeline.
A release pipeline split from the build, and labels that drive release notes
Two build files sit at the root, and the split between them tells you how releases happen.
build.sbt is the normal build definition for the project, and ci-release.sbt is a separate one used for the release pipeline. Splitting the release configuration out of the main build means the publishing steps, with their signing and their versioning, do not have to live in the same file as the everyday compile settings.
The release process is partly described by the pull request labels, and it is a small detail that says a lot about how the maintainers work. There are labels used for automated release descriptions, and two are documented here: chore if the pull request does not change any types and runtime semantics, and fix if the pull request merely fixes incorrect behavior.
So the commit type is not a convention you pick at commit time, it is a label the maintainers apply, and the release notes are assembled from that label. A change that alters types or runtime semantics is categorised differently from one that does not, which means the changelog a user reads can distinguish a behavioural change from a tidy-up without reading the diff.
The formatting story is automated too. There is an automated check for style conformance, you can run sbt checkfmt before opening a pull request, and if you hit trouble during the check you run sbt fmt and commit again.
The configuration files at the top level show how much of the toolchain is automated. There is a .scalafmt.conf for the formatter, a .scalafix.conf for the linter, a .scala-steward.conf for the dependency updater, a CODEOWNERS file, a CONTRIBUTORS.md, and a .git-blame-ignore-revs file, which is the mechanism for excluding formatting-only commits from the blame history so that a reformat does not obscure who last touched a line of logic.
There is also a .jvmopts and a .nvmrc, which is the interesting combination, since the documentation site is Node based and the library is Scala, and both toolchains are pinned in the repository.
Who uses it, and what the maintainers are asking for
The adopters section is a short list of logos with an invitation at the top: proud user of ToFu, feel free to add your company.
The logos correspond to Tinkoff, Vivid Money, Tele2, Konfy, Raiffeisen Bank Russia and Risk Management Solutions. That spread is worth noting for a functional programming library. Several of these are financial services firms, which is the population that historically pushed Scala and the tagless final pattern hardest, and it explains why the library invests in Cats MTL, fs2, ZIO and Log4Cats interop rather than betting on one effect system.
The contributors section is short as well. The copyright line reads Copyright the maintainers, 2019 to 2024, and the logos are credited to a named artist. There is a Discord badge and a Zulip badge, so the project runs both a chat server and a forum, and the Zulip badge points at a dedicated chat instance.
The build badge is a Scala CI workflow, and the release badges cover Maven Central and a Sonatype Nexus snapshots repository, so snapshots are published separately from releases.
The last thing worth mentioning is the documentation layout, because it is where the library's ambitions are visible. The quick start points at a microsite and at the examples directory rather than repeating anything inline, which is a sign of a project with more surface than a readme can hold. The microsite is the reference for the library's APIs, the examples directory is the executable version of the same thing, and the split by cats-effect version in the examples means you can read the code for your effect system without wading through the others.
For anyone considering adopting it, the structure of the module list is the thing to evaluate. The library is granular enough that you can take one module, and the fact that the whole pack is also available means a project can start with the umbrella and narrow down as its dependency graph settles.
Editorial conclusion
ToFu suits a Scala team that has committed to the tagless final style and wants to stop pulling in a whole effect-system stack for every utility, since the module split means the dependency graph matches what a project actually uses and the interop modules let the library sit alongside Cats, fs2 or ZIO rather than replacing them. It does not suit you if you want a single dependency to add, since the recommended path is picking a subset of roughly thirty artifacts, and it does not suit you if you are not ready to own two cats-effect lines in the same organisation. Before you adopt it, check which cats-effect version each of your services runs on, because the core utilities are published separately for each, and read the microsite rather than the readme, since that is where the per-module reference actually lives.
Frequently asked questions
What is the Tofu Scala library and what problem does it address?
It is a functional programming toolkit aimed at taming the complexity of the Tagless Final approach. Its answer is a fine grained module layout, where core utilities, concurrency, config, data, derivation, logging, caching and interop modules are separate artifacts you pick from rather than one library you depend on wholesale.
How do I add Tofu to an sbt build?
Add the whole utils pack with libraryDependencies += "tf.tofu" %% "tofu" % "latest version in badge", or specify an exact list of modules instead, such as tofu-kernel, tofu-core-ce3, tofu-concurrent, tofu-config, tofu-data, tofu-derivation, tofu-doobie, tofu-enums, tofu-env, tofu-streams, tofu-logging, tofu-memo and the interop modules.
What is the difference between the ce2 and ce3 Tofu modules?
The core utils modules come in both flavours, and you choose based on which cats-effect version you use, because the effect type appears in the signatures and the two lines are not interchangeable. The examples directory is split the same way, with separate ce2, ce3 and zio2 example sets.
How does the Tofu project build its documentation site?
With a Docusaurus instance run in Docker. The compose file builds from the repository root and publishes ports 3000 and 35729, and it mounts the docs, website blog, i18n, src, static, sidebars and docusaurus config directories from the host so the content can be edited without rebuilding the image.
How are Tofu release notes generated?
From pull request labels the maintainers apply. A chore label means the pull request does not change any types and runtime semantics, and a fix label means it merely fixes incorrect behavior, so the automated release description can distinguish a behavioural change from a tidy-up.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tofu-tf-tofu)Was this useful?