The point in which an entire abstract program defi...
# arrow
r
The point in which an entire abstract program defined in terms of kinds and type classes is made concrete to IO which usually happens in main or wherever you are given control in your framework of choice. If that doesn't make sense I can provide code examples in a sec
n
Thanks @raulraja for the explanation. So, to defer/suspend the effects, so that it executes in
main
or some such designated function marked with
IO
or similar monad.
Let's use contrived example to consolidate this understanding: We are writing a REST API for retrieving users profile like so:
/api/users/:username
In the
http
layer, the request is forwarded to:
Users.get(:username): IO[User]
in the
domain
layer, where it then does some business validation, and then forwards to the
database
layer:
DB.get(table: User, username: String): IO[User]
, where it interacts with external world and executes SQL and retrieves the
User
profile. Now, what if I do not use an
IO
monad? This thing could blow up anywhere, right? What are the disadvantages? I am trying to explain this to a colleague who has C++/Java background, and his question is like what difference does it make, when a program fails it fails! How does it matter? Appreciate your time @raulraja . Thanks!
r
This reddit includes a lot of justifications for working with IO vs just the environment. https://www.reddit.com/r/scala/comments/8ygjcq/can_someone_explain_to_me_the_benefits_of_io/
👍 1
But in practice with IO you have concurrency, async, effect and error control regardless of async unlike with try/catch
All your expressions are referentially transparent and pure so you can apply local reasoning about your program at the boundaries and you can also store your program in values and pass it around without fear of mutable state screwing it all up
That is because a value of IO has never been executed and it only does at the edge of the world
When you program with IO you program is a data structure resulting from the composition via map, flatMap, bind etc. whereas if you work in the environment each instructions is causing effects as functions are evaluated in the stack
fun printStuff(): Unit = println("stuff")
That is impure whereas
fun printStuff(): IO<Unit> = IO { println("stuff") }
it's pure
The IO version won't print anything if you run
printStuff()
and that makes a huge difference because now you can reason about that function knowing that when invoked it won't trigger effects. Also the
fun
can be replaced by a
val
and now you have a program in a value.