There are even more benefits to tagless final besides ones mentioned. The obvious one is that you can swap your runtime, like replace cats-effect IO with ZIO, if someone bites you. Another one is the ability to "enrich" your IO, e.g. introducing some environment by changing it to Kleisli[IO, YourContext, A], where YourContext can hold current request id, security information, etc. Then you introduce a typeclass to read the context in your code and keep everything else intact.
You can now implement the semantics of Kleisli using IOLocal hidden behind an abstraction, so that part is no longer an argument on the side of TF. Natchez works this way and gives you Trace[IO]. This can be done for request/security context as well, and works just fine IMO. I'm a TF fan myself, but mostly as a mechanism of constraining capabilities.
I think hexagonal architecture as a metaphor is much powerful than onion. I think Onion is lacking (would be nice if it would become much less popular) ... Basically what Hexagonal gives you, is partitioning for your layers. Of course every module would be a hexagon, but within a hexagon, it is implied that every facet is a different concern hence it should be well separated (namespace separation doesn't do it), whereas with onion metaphor, each layer is a big soup since the logical separation (namespaces seem just fine) it implies within each layer, it's a sure way to hell from a maintainability / testability perspective.
There are even more benefits to tagless final besides ones mentioned. The obvious one is that you can swap your runtime, like replace cats-effect IO with ZIO, if someone bites you. Another one is the ability to "enrich" your IO, e.g. introducing some environment by changing it to Kleisli[IO, YourContext, A], where YourContext can hold current request id, security information, etc. Then you introduce a typeclass to read the context in your code and keep everything else intact.
You can now implement the semantics of Kleisli using IOLocal hidden behind an abstraction, so that part is no longer an argument on the side of TF. Natchez works this way and gives you Trace[IO]. This can be done for request/security context as well, and works just fine IMO.
I'm a TF fan myself, but mostly as a mechanism of constraining capabilities.
Goated intro 😂 great job @rockthejvm
❤
Amazing
I think hexagonal architecture as a metaphor is much powerful than onion. I think Onion is lacking (would be nice if it would become much less popular) ... Basically what Hexagonal gives you, is partitioning for your layers. Of course every module would be a hexagon, but within a hexagon, it is implied that every facet is a different concern hence it should be well separated (namespace separation doesn't do it), whereas with onion metaphor, each layer is a big soup since the logical separation (namespaces seem just fine) it implies within each layer, it's a sure way to hell from a maintainability / testability perspective.