Skip to content

Instantly share code, notes, and snippets.

@tvoklov
Created November 7, 2022 10:59
Show Gist options
  • Save tvoklov/169b10d317716f70b9af1009e1df745c to your computer and use it in GitHub Desktop.
Save tvoklov/169b10d317716f70b9af1009e1df745c to your computer and use it in GitHub Desktop.

ZIO is not made for FP developers

I've recently revisited a project that, out of curiosity, I tried writing in ZIO instead of cats effect, and the more I looked at it the more I got confused how weird and seemingly unwieldy this library is.

Here's an example: I have to get some data from an http server and send it into a kafka queue.

That function, written with cats effect, will look like this:

def readAndSendToKafka[F[] : Async](httpClient: Client[F], kafkaProducer: Producer[F])(httpUrl: Url, kafkaTopic: String): F[Unit] =
  for {
    data <- httpClient.get(httpUrl)
    _ <- kafkaProducer.produce(kafkaTopic)(data)
  } yield ()

And here's the same task, but written using ZIO:

def readAndSendToKafka(httpUrl: Url, kafkaTopic: String): ZIO[Any with Client with Producer, Throwable, Unit] =
  for {
    // if the service doesn't have companion object methods
    client <- ZIO.service[Client]
    data <- client.get(httpUrl)
    
    // or if it does
    _ <- Producer.produce(kafkaTopic)(data)
  } yield ()

If you're an object-oriented programmer, this probably doesn't seem that bad to you. In fact, you'd probably argue that ZIO's solution to getting services is cleaner and easier to use than cats'. The reason is that in ZIO there are two patterns behind this cool new thing named an Environment that operates on top of scala macros (wow, I heard these are cool) and gives you compile-time checks that you have provided everything your code requires. These two patterns are called Singleton and Dependency Injection.

For an FP programmer, even if they are ok with singletons (which they shouldn't be), DI always reintroduces memories of why they moved to functional programming. Dependency injection is the most annoying tool in an OOP programmer's toolbox. It is the reason why they feel comfortable shipping code that will get broken, why costs rise when you have to refactor old code and it is the main reason why tests are seen as something that is required instead of heavily suggested.

So, ZIO comes in and tells you "Ok, I will fix one part of your DI issues by making sure that at compile-time you know which dependencies you have to inject". Problem solved, right? Well, for OOP, this is the silver bullet of writing tests that are easy to come back to, services that are easy to separate and, in general, code that is easy to understand and ship. But even compile-time checked DI is not functional programming.

This is the part of this text where I have to put down my "being-annoying" glasses and say that I am fine with people bending the rules of their tools as long as (*puts the glasses on*) the product they create gets better. Notice how I emphasized the word better? Yeah, I think it's a good idea to engrain it into the heads of the OOP world that breaking the rules is bad and should be outweighed by good things. If the product doesn't acquire any value then you breaking the rules it is built upon makes it worse.

Dependency injection breaks the most basic rule of pure FP - functions should be as pure as possible and strive to use their arguements over the outside world's state. A library that is built on DI and makes it convinient to use encourages its users to use DI. Therefore ZIO encourages programmers to break the paradigm of pure functional programming.

I have nothing against ZIO (even though I'd never use it again). The only thing about it I dislike is when people claim it is an FP library. The guys making ZIO know it isn't functional. They embrace it and good for them. I'd rather have OOP programmers use ZIO than Spring. At least ZIO is more stable.

When it comes to FP programmers, though, I would suggest using a library that encourages FP patterns over outdated and inherently broken OOP patterns. You know, like cats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment