Is there a way to place some impure code inside pure functions?

偶尔善良 提交于 2021-02-01 05:19:10

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!