Difference between revisions of "How to get rid of IO"
m (Minor formatting changes; extra reference added) |
m ("Fine print" removed) |
||
Line 99: | Line 99: | ||
* [[Tutorials#Practical_Haskell|Tackling the awkward squad]] |
* [[Tutorials#Practical_Haskell|Tackling the awkward squad]] |
||
* [[IO at work]] |
* [[IO at work]] |
||
− | ---- |
||
− | <br> |
||
− | <tt><sub> |
||
− | <p>''What you weren't told immediately, and why''.</p> |
||
− | |||
− | <p>Right now, there is <strike>a function</strike> an entity in Haskell 2010 which does just that. However, it should ''not'' be abused in this way - it must only be used to wrap foreign I/O procedures that behave like pure Haskell functions i.e. they have ''no visible side effects''. Since this cannot be done by the compiler, it's the user's responsibility to ensure this property is satisfied.</p> |
||
− | |||
− | <p>Therefore only very-experienced programmers, usually those writing system-level programs or libraries should use this entity - if you've never written anything as complex as e.g. a [https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.13.9123&rep=rep1&type=pdf webserver] or an [https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.155.967&rep=rep1&type=pdf operating system], you are most likely ''not qualified'' enough to use it.</p> |
||
− | |||
− | <p>Otherwise, more information about this fraught entity can be found [https://hoogle.haskell.org/?hoogle=unsafeLocalState here].</p> |
||
− | </sub></tt> |
||
[[Category:FAQ]] |
[[Category:FAQ]] |
Latest revision as of 05:08, 6 April 2022
I have something of type IO a
, but I need something of type a
, so how do I get rid of that annoying IO
type?
...so in other words, you've got e.g. thatIOThingie :: IO Datum
, but regularThingie :: Datum
is what the rest of your code is expecting.
Well, there are a few ways to do this...
Abstracting dependent code as functions
Regarding the code expecting regularThingie
: If you're able to make it into a function e.g:
whateverYouNeedToDoWith :: Datum -> Value
- Use
Functor.fmap
- Using your new function:
fmap whateverYouNeedToDoWith thatIOThingie
- Use lifting combinators
- You can also use:
liftM whateverYouNeedToDoWith thatIOThingie
- ...you've got several
IO
things to deal with? No problem:
liftM3 thePurposeOfUsing thisIOTingie thatIOThingie anotherIOThingie
Using I/O actions more directly
If changing the code expecting regularThingie
is impractical, then:
- Use
do
-notation
do regularThingie <- thatIOThingie return (whateverYouNeedToDoWith regularThingie)
- Use the basic bind operations for I/O:
(>>=)
and(>>)
:
thatIOThingie >>= \ regularThingie -> return (whateverYouNeedToDoWith regularThingie)
Yes, each of those techniques produces more IO
thingies. That's the real beauty of it: if you have a value that is dependent on the environment (i.e. a IO
thingie value) you can use it as a regular value as shown above, but the result will always be another IO
value (The result will depend on the environment because it uses a value dependent of the environment).
This is no problem, just accept it.
So by using those techniques, you can use I/O while keeping all the safety properties of a pure functional programming language.
You don't understand: I have an IO String
and I just want to remove the IO
bit - how can I do that?
Alright, here's the short answer: You don't.
More information, please
If there was a way to do it e.g. doItNow :: IO a -> a
then everyone else can also use it:
clickHereToWin = doItNow (stealCreditCardDetails >> selectRandomPrize)
Is getting that darn String
really that important - why are you even using Haskell?
That is why you cannot get rid of IO
in Haskell; think of it instead as a special safety-belt which stops your programs falling out of the computer-carnival ride. Albert Lai posted another good explanation on comp.lang.functional:
I now want to address the real question: how do we write useful programs if we can't get rid of the IO
tag?
Normally, we write the core algorithm as a function. This does not perform I/O. It just needs to take input data from parameters, and return the answer.
The main
program will be responsible for I/O. It reads strings from the input file, turns them into internal data types (e.g. numbers), gives them to the function, takes the return value of the function, and prints the answer.
The main
program is required to be an IO
guy anyway, so there is no extra difficulty doing I/O inside it, and there is no need to turn IO String
into String
inside it either.
So the biggest parts of your Haskell programs should be pure, non-I/O functions which crunch all the data, leaving a thin layer of I/O code for retrieving the necessary information (e.g. from a database) and displaying the results (e.g. to a V.R. headset).
Instead of finding a way to get rid of IO
, focus on keeping the I/O layer of your Haskell programs as thin as possible by maximising the code which is pure.
(Alternately, if you could get rid of IO
then so could everyone else...)
See also
- Introduction to IO
- Avoiding IO - Avoiding IO in the first place is a good thing, and we tell you how to achieve that
- Tackling the awkward squad
- IO at work