|
Ok, so you make pure functions and turn them dirty, so to speak. Makes sense. In our case, almost all core code depends on various parameters, which come from the database. For example, GB recently left the EU so everything involving GB is now processed under different rules, except old stuff which has to be processed under the old rules. Thus being part of EU or not is a date-dependent database query (it already was, not the first time a country's EU status has changed). So if I get your explanation correctly, I'd code the core logic as if these parameters were pure, side-effect free, which would make the core logic side-effect free. In the case above, I'd pass a function which maps a (pure) date and string into a (pure) bool, to test for EU membership. I'd then turn that whole thing dirty via the IO thingy, passing "IO parameters" and receiving "IO results", so I can pass it my EU test function which does a database query. edit: And I presume my "dirty" database-connecting function can also mutate things, so it can do caching. Don't want to hit that database too often. |
i doubt you could make (or really, even want to make) `checkEUMembership` pure, I'm guessing it'd involve a DB lookup of some kind.
in general, you can't always "pull out all the IO" into an only-pure-logic "core"; like if you want to look up one thing and then look up another thing based on the result of the first lookup. and that's okay!
i'm not going to write a whole monad tutorial, but using an `IO Foo` is kind of like using a `Promise<Foo>`¹; you do stuff like this (in JS syntax):
"do-notation" lets you avoid callback hell, similarly to async/await.---
¹ Unfortunately, JS's Promise#then mixes two things:
• "dirtying" a pure function:
which in Haskell would use • piping the result into another side-effecting function: which in Haskell would use the "bind" operator: