I think the best explanation I've heard was actually fairly recently on SO. IO Foo
is a recipe for creating a Foo
. Another common, more literal, way of saying this is that it is a "program that produces a Foo
". It can be executed (many times) to create a Foo
or die trying. The execution of the recipe/program is what we ultimately want (otherwise, why write one?), but the thing that is represented by an IO
action in our code is the recipe itself.
That recipe is a pure value, in the same exact sense that a String
is a pure value. Recipes can be combined and manipulated in interesting, sometimes astonishing, ways, but the many ways these recipes can be combined (except for the blatantly non-pure unsafePerformIO
, unsafeCoerce
, etc.) are all completely referentially transparent, deterministic, and all that nice stuff. The resulting recipe depends in absolutely no way whatsoever on the state of anything other than the recipes that it was built up from.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…