简体   繁体   中英

Haskell function with type IO Int -> Int, without using unsafePerformIO

I have a homework question asking me:

Can you write a Haskell function with type IO Int -> Int (without using unsafePerformIO)? If yes, give the function; if not, explain the reason.

I have tried to write such a function:

test :: IO Int -> Int
test a = do 
    x <- a
    return x

But this does not work. I have tried to make it work for a while, and I can't, so I gather that the answer to the question is no. But, I do not understand why it is not possible. Why doesn't it work?

The only functions of type IO Int -> Int are uninteresting, since they must ignore their argument. that is, they must be equivalent to

n :: Int
n = ...

f :: IO Int -> Int
f _ = n

(Technically, there's also fx = x `seq` n , as @Keshav points out).

This is because there's no way to escape the IO monad (unlike most other monads). This is by design. Consider

getInt :: IO Int
getInt = fmap read getLine

which is a function which reads an integer from stdin. If we could write

n :: Int
n = f getInt

this would be an integer value which can "depend" on the IO action getInt ... but must be pure, ie must do no IO at all. How can we use an IO action if we must do no IO at all? It turns out that we can not use it in any way.

The only way to do such operation meaningfully is to allow the programmer break the "purity" contract, which is the main thing behind Haskell. GHC gives the programmer a few "unsafe" operations if the programmer is bold enough to declare "trust me, I know what I am doing". One of them is "unsafePerformIO". Another is accessing the realWorld# low-level stuff in GHC.* modules (as @Michael shows above). Even the FFI can import a C function claiming it to be pure, essentially enabling the user to write their own unsafePerformIO .

Well, there are two ways...

If you are happy with a constant function, use this:

test :: IO Int -> Int
test a = 42

Otherwise, you have to do the same thing that unsafePerformIO does (or more specifically unsafeDupablePerformIO ).

{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}

import GHC.Base (IO(..), realWorld#)

unIO (IO m) = case m realWorld# of (# _, r #) -> r

test :: IO Int -> Int
test a = (unIO a) + 1

Have fun...

Disclaimer: this is all really hacky ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM