> external world has tendencies to fail, needs to be retried, coordinated with other things.
This is exactly why I'm so aggressive in splitting IO from non-IO.
A pure function generally has no need to raise an exception, so if you see one, you know you need to fix your algorithm not handle the exception.
Whereas every IO action can succeed or fail, so those exceptions need to be handled, not fixed.
> You have to fake all these issues.
You've hit the nail on the head. Every programmer at some point writes code that depends on a clock, and tries to write a test for it. Those tests should not take seconds to run!
In some code bases the full time is taken.
handle <- startProcess
while handle.notDone
sleep 1000ms
check handle.result
In other code-bases, some refactoring is done, and fake clock is invented.
fakeClock <- new FakeClock(10:00am)
handle <- startProcess(fakeClock);
fakeClock.setTime(10:05am)
waitForProcess handle
Why not go even further and just pass in a time, not a clock?
let result = process(start=10:00am, stop=10:05)
Typically my colleagues are pretty accepting of doing the work to fake clocks, but don't generalise that solution to faking other things, or even skipping the fakes, and operating directly on the inputs or outputs.
Does your algorithm need to upload a file to S3? No it doesn't, it needs to produce some bytes and a url where those bytes should go. That can be done in unit-test land without any IO or even a mocking framework. Then some trivial one-liner higher up the call-chain can call your algorithm and do the real S3 upload.
I completely agree, but I still question the purpose of FP languages. Writing the S3 upload code is quite hard, if you really want to handle all possible error scenarios. Even if you use whatever library for that, you still need to know about which errors can it trigger and which need to be handle, and how to handle them. The mental work can be equal to the core function for generating the file. In any language, I'd separate these two pieces of code, but I'm not sure if I'd want to handle S3 upload logic with all the error handling in a FP language. That said, I've not used Clojure yet and that seems like a very pragmatic language, which might be actually usable even for these parts of the code.
This is exactly why I'm so aggressive in splitting IO from non-IO.
A pure function generally has no need to raise an exception, so if you see one, you know you need to fix your algorithm not handle the exception.
Whereas every IO action can succeed or fail, so those exceptions need to be handled, not fixed.
> You have to fake all these issues.
You've hit the nail on the head. Every programmer at some point writes code that depends on a clock, and tries to write a test for it. Those tests should not take seconds to run!
In some code bases the full time is taken.
In other code-bases, some refactoring is done, and fake clock is invented. Why not go even further and just pass in a time, not a clock? Typically my colleagues are pretty accepting of doing the work to fake clocks, but don't generalise that solution to faking other things, or even skipping the fakes, and operating directly on the inputs or outputs.Does your algorithm need to upload a file to S3? No it doesn't, it needs to produce some bytes and a url where those bytes should go. That can be done in unit-test land without any IO or even a mocking framework. Then some trivial one-liner higher up the call-chain can call your algorithm and do the real S3 upload.