I just don’t get it. Those are Haskell effect systems, and Haskell relies on IO. How is this a practical problem?
Maybe you can show me on this example, where it arises as a problem. Let’s imagine some standard web API.
addCar :: Car -> IO Int
getCar :: Int -> IO (Maybe Car)
version :: IO Version
All those functions can, in theory, execute any IO function, but they probably don’t do anything creative. But still, let’s get fancy.
addCar :: (WriteDB :> es) => Car -> Eff es Int
getCar :: (ReadDB :> es) => Int -> Eff es (Maybe Car)
version :: Eff es Version
Are there any upgrades here? No, not much. Of course, we were using a database and not much else, and of course getCar
doesn’t write to it! And version
is pretty much pure.
Now imagine the problem scales in space and time. We have many more endpoints (hundreds, maybe) and maybe we didn’t touch the codebase for a year.
Then there is a bug, some invalid state in our database. Where do we look for it? For sure, not in ReadDB
endpoints, they don’t change the state of our database! We eventually find a bug, maybe a little bit faster, thanks to that insight.
The next task is to add some tests, to make bugs disappear. We could be smart and add tests only for WriteDB
endpoints, as ReadDB
don’t really do anything. But metrics are a thing in IT and generally triumph reason, so let’s test everything.
Now the tests are slow, they run against a real database. We could speed them up by running some in parallel, but for which tests is it safe? Of course, for ReadDB
ones!
Above is, of course, what we imagine effect systems could do for us. Reality will be more complex and troublesome. But it illustrates that whether, if we, “in the end”, program against IO, it doesn’t even matter.
I hope it is a better example to debate “Why use an effect system?”