Difference between revisions of "How to get rid of IO"

From HaskellWiki
Jump to navigation Jump to search
(Small text about that really FAQ)
 
m ("Fine print" removed)
 
(8 intermediate revisions by 3 users not shown)
Line 1: Line 1:
  +
<i>I have something of type <code>IO a</code>, but I need something of type <code>a</code>, so how do I get rid of that annoying <code>IO</code> type?</i>
== Question ==
 
  +
<br>
  +
<br>
  +
...so in other words, you've got e.g. <code>thatIOThingie :: IO Datum</code>, but <code>regularThingie :: Datum</code> is what the rest of your code is expecting.
   
  +
Well, there are a few ways to do this...
I have something of type <hask>IO a</hask>, but I need something of type <hask>a</hask>
 
How can I get that?
 
   
  +
== Abstracting dependent code as functions ==
== Answer ==
 
  +
Regarding the code expecting <code>regularThingie</code>: If you're able to make it into a function e.g:
   
You can get rid of it, but we don't tell you, how, since it is certainly not what need.
 
It is the special safety belt of Haskell, that you cannot get rid of IO!
 
Nonetheless, the biggest parts of Haskell programs are and should be non-IO functions.
 
This is achieved by plugging together IO functions and non-IO functions
 
using atomic combinator functions like <hask>(>>=)</hask>.
 
If that scares you, you can hide that in the [[do notation]],
 
which will then look quite conveniently as:
 
 
<haskell>
 
<haskell>
  +
whateverYouNeedToDoWith :: Datum -> Value
do text <- readFile "foo"
 
writeFile "bar" (someComplicatedNonIOOperation text)
 
 
</haskell>
 
</haskell>
  +
Btw. using the combinators this would look like
 
  +
* Use <code>Functor.fmap</code>
  +
  +
:Using your new function:
  +
  +
:<haskell>
  +
fmap whateverYouNeedToDoWith thatIOThingie
  +
</haskell>
  +
  +
* Use ''lifting combinators''
  +
  +
:You can also use:
  +
  +
:<haskell>
  +
liftM whateverYouNeedToDoWith thatIOThingie
  +
</haskell>
  +
  +
:...you've got several <code>IO</code> things to deal with? No problem:
  +
  +
:<haskell>
  +
liftM3 thePurposeOfUsing thisIOTingie thatIOThingie anotherIOThingie
  +
</haskell>
  +
  +
== Using I/O actions more directly ==
  +
If changing the code expecting <code>regularThingie</code> is impractical, then:
  +
  +
* Use <code>do</code>-notation
  +
  +
:<haskell>
  +
do regularThingie <- thatIOThingie
  +
return (whateverYouNeedToDoWith regularThingie)
  +
</haskell>
  +
  +
* Use the basic ''bind'' operations for I/O: <code>(>>=)</code> and <code>(>>)</code>:
  +
  +
:<haskell>
  +
thatIOThingie >>=
  +
\ regularThingie -> return (whateverYouNeedToDoWith regularThingie)
  +
</haskell>
  +
  +
Yes, each of those techniques produces more <code>IO</code> thingies. That's the real beauty of it: if you have a value that is dependent on the environment (i.e. a <code>IO</code> <strike>thingie</strike> value) you can use it as a regular value as shown above, but the result will always be another <code>IO</code> 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.
  +
----
  +
----
  +
<i>You don't understand: I have an <code>IO String</code> and I just want to remove the <code>IO</code> bit - how can I do that?</i>''
  +
<br>
  +
<br>
  +
Alright, here's the short answer: '''You don't.'''
  +
  +
== More information, please ==
  +
  +
If there was a way to do it e.g. <code>doItNow :: IO a -> a</code> then everyone else can also use it:
  +
 
<haskell>
 
<haskell>
  +
clickHereToWin = doItNow (stealCreditCardDetails >> selectRandomPrize)
writeFile "bar" . someComplicatedNonIOOperation =<< readFile "foo"
 
 
</haskell>
 
</haskell>
   
  +
Is getting that darn <code>String</code> '''really''' that important - why are you even using Haskell?
=== What we didn't tell you at the beginning ===
 
  +
<br>
  +
<br>
  +
That is why you cannot get rid of <code>IO</code> in Haskell; think of it instead as a special safety-belt which stops your programs falling out of the computer-carnival ride. [[AlbertLai|Albert Lai]] posted another good explanation on comp.lang.functional:
  +
  +
<div style="border-left:1px solid lightgray; padding: 1em" alt="blockquote">
  +
I now want to address the real question: how do we write useful programs if we can't get rid of the <code>IO</code> 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 <code>main</code> 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 <code>main</code> program is required to be an <code>IO</code> guy anyway, so there is no extra difficulty doing I/O inside it, and there is no need to turn <code>IO String</code> into <code>String</code> inside it either.
  +
</div>
  +
  +
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 <code>IO</code>, 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 <code>IO</code> then so could everyone else...)
Btw. The function that answers your initial question is <hask>unsafePerformIO</hask>.
 
It is however not intended for conveniently getting rid of the <hask>IO</hask> constructor.
 
It must only be used to wrap IO functions that behave like non-IO functions,
 
Since this property cannot be checked by the compiler, it is your task and thus the <hask>unsafe</hask> part of the name.
 
(Some library writers have abused that name component for [[partial function]]s. Don't get confused!)
 
You will only need this in rare cases and only experienced programmers shall do this.
 
   
 
== See also ==
 
== 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
 
* [[Avoiding IO]] - Avoiding IO in the first place is a good thing, and we tell you how to achieve that
  +
* [[Tutorials#Practical_Haskell|Tackling the awkward squad]]
* http://www.haskell.org/wikisnapshot/ThatAnnoyingIoType.html
 
  +
* [[IO at work]]
* http://www.haskell.org/wikisnapshot/UsingIo.html
 
   
 
[[Category:FAQ]]
 
[[Category:FAQ]]
  +
[[Category:Monad]]

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