问题
IO
, just like Maybe
, is just an instance of Monad
. On the other hand we have all data constructors for Maybe
(Just
and Nothing
), but no constructors for IO
. Reader
and Writer
do not export constructors too, they have functions, which return instance of this type (reader
and writer
) and more importantly runReader
and runWriter
, which unwrap computation result from Monad.
Is there a way to unwrap IO Monad? I would like to have pure function which do some impure IO computations under the hood. Currently I am able to do this with most of the Monads
I know one example of such tricky function: Debug.Trace.trace
回答1:
unsafePerformIO :: IO a -> a
in System.IO.Unsafe
(base).
Use it with caution and read the description in the documentation carefully.
回答2:
The correct answer is
No, you can't!
Well, yes, GHC has a thing called unsafePerformIO
, but this is not part of the Haskell standard, merely a hack to allow certain “morally pure” functions from other languages to be called using the foreign function interface, and reflect the type of those functions with the type they would have if you'd written them straight in pure Haskell.
Note that “unwrapping the IO
monad” would not simply give you the result of that computation. If IO
were a public-constructors type, it would actually look (conceptually) something like the following:
data IO' a =
WriteToFile FilePath String
| PutStr String
| WithStdLine (String -> IO' a)
| ...
| SequenceIO (IO' ()) (IO' a)
Pattern matching on such an IO' a
value would normally not give you access to anything of type a
, it would merely give you some description of actions to be performed, and perhaps some function that could possibly yield an a
value from intermediate results obtained from the environment.
The only way to actually get useful work done would then still be like it is now: by binding it to something like the main
action, which then executed by some “real world entity” (the runtime).
If you want to implement an algorithm that describes a proper mathematical (i.e. pure) function but seems to lend itself to an imperative programming style with mutation etc., then you should not implement this in the IO
monad at all. You might well be able to just implement it in ordinary pure Haskell98 by just choosing suitable data structures, or perhaps it makes sense to use the ST monad to achieve e.g. array updates with the same performance they'd have in imperative languages.
回答3:
Is there a way to place some impure code inside pure functions?
What if there was a way to do this?
What if everyone else could use it?
Would you be willing to sit down and scrutinise the sources of each and every library and program you use to check they are safe?
If you are, then Haskell probably isn't for you - I suggest you have a look at languages like Standard ML or OCaml...
You're still here?
Alright then, there's an alternative approach which you can use: abstract out the pure functions from the impure code. It's possible to do this because functions are first-class values in Haskell - in particular, functions can be used as arguments e.g:
fmap :: Functor f => (a -> b) -> f a -> f b
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
As your powers of abstraction improve, you'll delegate more and more work to pure code (in the form of functions), with just a small cohort of definitions being tainted by effects (including main :: IO ()
). It requires some extra effort to start with, but the long-term rewards are substantial...
来源:https://stackoverflow.com/questions/41522491/is-there-a-way-to-place-some-impure-code-inside-pure-functions