1

When I'm writing code in the form of IO Monad, I wonder what's real value of it.For example I have a function as def something(in: In): IO[Out]; my function is a pure function that **returns an impure recepie(just another function) which could be composed with other receipes, and finally triggered.

Is postponing the side-effects the best I get? In other words, what's the difference in the value between a function with side-effects, and a pure function which return an impure function? In my eyes both approches could be testable, trackable, debugable (the IO version could be harder, as stacktraces are not as helpful as simple stack-calls).

3
  • One way to look at it is as a form of layering. The IO Monad encapsulates the "impure world", and represents side effects (the various possible impure recipes) as abstract values (literally think of monadic values as opaque points (elements) in some mathematical set). Then the upper layer can maintain purity by working with these abstractions. In a sense, the upper layer doesn't really "know" that each recipe is "just another function", it just treats them as these abstract values. – Filip Milovanović Jun 28 '20 at 21:01
  • A monad isn't "impure". Consider it as an abstract reference to a value that can be passed to a pure function that you give it using the bind function. That way, everything can be pure (functions have no direct side effects and are always referentially transparent) without preventing useful operations. – occipita Jun 30 '20 at 12:20
  • The IO Monad is the reason you can have a language where functions don't have side effects. If you didn't have the IO monad, you'd have to let functions have side effects, and then instead of Haskell, you'd have Python with different syntax. – user253751 Jun 30 '20 at 18:01
3

In addition to what Jacques said about the side effects being explicit in your type system, you also get referentially transparent side effects. This means you can store effects or groups of effects in variables and lists and end up with the exact same result. The cats State documentation has a good example of this for the State monad.

One thing this enables that isn't immediately apparent how useful it is, is the order you specify your effects can be different from the order they execute, and some you specify may not end up executing at all.

Let's say you have a request that connects to a list of servers, does some processing, and returns the first successful result. The request contains all the information needed to do the processing step, so you specify that effect first. The list of servers is read from config, so that's handled somewhere else, perhaps memoized. You can specify all the effects for connecting to the servers in one place, but then later choose to run them one at a time, or in parallel, or with retries.

In other words, it makes it a lot easier to separate the concerns of what gets executed from what order it gets executed.

5

The primary benefit is you get the IO explicitly in the type of the function. So you know that any function without IO in the signature is side-effect free. The pure parts of the program is guaranteed to be pure and side-effect free by the type system.

1
  • Even functions with IO are side effect free. They are functions which return values which describe side effects. You can do let x = putStrLn "hello world!" all day and it won't print anything. – user253751 Jun 30 '20 at 18:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.