【问题标题】:Haskell function with type IO Int -> Int, without using unsafePerformIOIO Int -> Int 类型的 Haskell 函数,不使用 unsafePerformIO
【发布时间】:2014-11-29 08:05:02
【问题描述】:

我有一个作业问题问我:

你能写一个 IO Int -> Int 类型的 Haskell 函数吗(不使用 unsafePerformIO)?如果是,请给出功能;如果不是,请说明原因。

我曾尝试写过这样的函数:

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

但这不起作用。我试图让它工作一段时间,但我做不到,所以我认为这个问题的答案是否定的。但是,我不明白为什么这是不可能的。为什么它不起作用?

【问题讨论】:

  • 提示:函数不必使用所有参数。
  • 对不起,我不明白这对我有什么帮助。据我了解,我的函数只有一个参数,那就是“IO Int”。如果我这样做:test :: IO Int -&gt; Inttest a b = b 我收到以下错误:The equation(s) for ‘test’ have two arguments, but its type ‘IO Int -&gt; Int’ has only one
  • 该函数被允许使用其一个参数中的零个。它没有两个参数,所以你当然不能编写一个带有两个参数的模式。
  • 啊。我曾想过这一点,但认为这是微不足道的。再想想,这似乎是正确的答案。谢谢!
  • 另一个,也许更邪恶的提示:所有 Haskell 类型都有人居住:3 可能不是问题正在寻找的答案,但...

标签: haskell io


【解决方案1】:

IO Int -&gt; Int 类型的唯一函数是无趣的,因为它们必须忽略它们的参数。也就是说,它们必须等价于

n :: Int
n = ...

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

(从技术上讲,还有f x = x `seq` n,正如@Keshav 指出的那样)。

这是因为无法逃脱 IO monad(与大多数其他 monad 不同)。这是设计使然。考虑

getInt :: IO Int
getInt = fmap read getLine

这是一个从标准输入读取整数的函数。如果我们可以写

n :: Int
n = f getInt

这将是一个整数值,它可以“依赖”于 IO 操作 getInt... 但必须是纯的,即根本不能执行任何 IO。如果我们根本不做 IO,我们如何使用 IO 操作?事实证明,我们不能以任何方式使用它。

有意义地进行此类操作的唯一方法是允许程序员打破“纯”契约,这是 Haskell 背后的主要内容。如果程序员足够大胆地声明“相信我,我知道我在做什么”,GHC 会给程序员一些“不安全”的操作。其中之一是“unsafePerformIO”。另一个是访问GHC.* 模块中的realWorld# 低级内容(如@Michael 上面所示)。甚至 FFI 也可以导入一个声称它是纯的 C 函数,本质上使用户能够编写自己的 unsafePerformIO

【讨论】:

  • f x = seq x n 怎么样?这似乎不等同于f _ = n。 (f = undefined 也不值,但那是另一回事……)
  • @KeshavKini:这几乎是等价的,因为seq x n 的结果要么是常量n,要么是底部。就此而言,f _ = undefined 也差不多。
  • @JohnL:我的意思是,如果f x = seq x n,那么f x 的值取决于x 是什么,而对于某些n :: Int,定义为f _ = n 的函数不能依赖关于它的论点的任何事情。对于某些n :: Intf = undefined 也与定义为f _ = n 的所有函数不同,因为在后一种情况下,f 本身(未应用)标准化,而undefined 没有。然而f x = seq x nf = undefined 都是类型签名f :: IO Int -&gt; Int 的有效定义,所以说IO Int -&gt; Int 类型的所有函数都必须等价于f _ = n 似乎是不正确的。
  • @KeshavKini 我有点同意,但是undefined\_-&gt;undefined 之间的区别并不是很重要,并且仅在涉及seq 时才相关。如果我们要非常挑剔,undefined 也不同于error "a"。我们还可以考虑无限不同的值error "some string"。那为什么不包括涉及trace的表达式呢?
  • 关于 FFI “基本上使用户能够编写自己的 unsafePerformIO” - 如果只有 FFI 有能力:那么 unsafePerformIO 将不需要作为原始定义...
【解决方案2】:

嗯,有两种方法……

如果您对常量函数感到满意,请使用:

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

否则,您必须执行与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

玩得开心……

免责声明:这一切都非常hacky ;)

【讨论】:

猜你喜欢
  • 2014-06-02
  • 1970-01-01
  • 2018-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多