JIT: What’s an IO?

At this point I could turn you over to the ZIO and Cats Effect websites and their documentation, and you might do pretty well. But instead of doing that just yet, I’d like to help you out a little more.

The first thing to say is that before you’re 100% ready for a ZIO, it will help to look at an IO.

A bit of FP history

As a wee bit of FP history, back in the early days of Haskell, some really smart people were trying to figure out how to handle I/O in a “pure” way. Every other function in Haskell was pure ... and then there was I/O.

Long story short, they invented a concept called an IO type. To start with, you can think of IO as being like Try or Either: it’s a type you want to use when you communicate with the outside world, or run any other side effect. So wherever I have function signatures like this in this book:

def readFile(f: String): Try[String]
def writeFile(s: String): Try[Unit]

conceptually, you can replace those Try types with the IO type:

def readFile(f: String): IO[String]
def writeFile(s: String): IO[Unit]

This is a slight oversimplification, but not too much. For example, in Functional Programming, Simplified, I show how to write an IO class and use it in for expressions, just like we’ve used Try in this book.

Three keys about IO types

Fast-forward to today, and there are three important things to know about IO types:

  1. They were created to let you reason about your I/O and side-effect code using algebraic substitution.
  2. One thing I haven’t mentioned yet is that you can’t mix Option, Try, and Either values inside for expressions, but, you can use as many IOs as you need.
  3. To help with Item 2, IOs are like Try and Either, but on steroids. They are much more powerful than those types, with tons of built-in features. (Types like Try and Either can be written in less than a page of Scala code, but IO types have been developed over years of effort.)

So let’s talk about IO types a little bit.