简体   繁体   English

具有IO Int-> Int类型的Haskell函数,而无需使用unsafePerformIO

[英]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)? 您可以编写IO Int-> Int类型的Haskell函数(不使用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. IO Int -> Int类型的唯一函数是无趣的,因为它们必须忽略其参数。 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). (从技术上讲,还有fx = x `seq` n Keshav指出的fx = x `seq` n )。

This is because there's no way to escape the IO monad (unlike most other monads). 这是因为无法逃避IO monad(与大多数其他monad不同)。 This is by design. 这是设计使然。 Consider 考虑

getInt :: IO Int
getInt = fmap read getLine

which is a function which reads an integer from stdin. 这是一个从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. 这将是一个整数值,可以“取决于” IO操作getInt ...,但必须是纯值,即必须完全不做任何IO。 How can we use an IO action if we must do no IO at all? 如果我们根本不需要执行任何IO操作,该如何使用IO操作? 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. 有意义地执行此操作的唯一方法是允许程序员打破“纯度”合同,这是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". 如果程序员足够大胆地声明“相信我,我知道我在做什么”,GHC会为程序员提供一些“不安全”的操作。 One of them is "unsafePerformIO". 其中之一是“ unsafePerformIO”。 Another is accessing the realWorld# low-level stuff in GHC.* modules (as @Michael shows above). 另一个正在访问GHC.*模块中的realWorld#低级内容(如上面的@Michael所示)。 Even the FFI can import a C function claiming it to be pure, essentially enabling the user to write their own unsafePerformIO . 甚至FFI都可以导入声称其为纯函数的C函数,从本质上使用户能够编写自己的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 ). 否则,您必须做与unsafePerformIO相同的事情(或更具体地说,是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 ;) 免责声明:这真的很hacky;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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