有没有办法在纯函数中放置一些不纯的代码?

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

IO,就像Maybe一样,只是Monad的一个实例。另一方面,我们拥有 MaybeJustNothing)的所有数据构造函数,但没有 IO 的构造函数。 ReaderWriter 也不导出构造函数,它们有函数,return 这种类型的实例(readerwriter)更重要的是 runReaderrunWriter,解包来自 Monad 的计算结果。

有没有解包 IO Monad 的方法?我想要纯函数,它可以在后台进行一些不纯的 IO 计算。目前我可以用大多数 Monads

我知道这种棘手函数的一个例子:Debug.Trace.trace

unsafePerformIO :: IO a -> aSystem.IO.Unsafe(基础)中。

慎用,仔细阅读documentation中的说明。

正确答案是

不,你不能!

嗯,是的,GHC有一个叫unsafePerformIO的东西,但这是不是的部分Haskell 标准,只是一个 hack,允许使用外部函数接口调用来自其他语言的某些“道德上纯”的函数,并反映这些函数的类型,如果您编写的话,它们将具有的类型他们纯正 Haskell.

请注意,“展开 IO monad” 而不是 只会给你计算的结果。如果 IO 是一个 public-constructors 类型,它实际上(概念上)看起来像下面这样:

data IO' a =
    WriteToFile FilePath String
  | PutStr String
  | WithStdLine (String -> IO' a)
  | ...
  | SequenceIO (IO' ()) (IO' a)

这种 IO' a 值的模式匹配通常不会让您访问 a 类型的任何内容,它只会给您一些 要执行的操作的描述,也许还有一些函数可能会产生 a 来自从环境中获得的 中间结果。

真正完成有用工作的唯一方法仍然像现在一样:通过将它绑定到类似 main 操作的东西,然后由一些“现实世界实体”(运行时)执行).

如果你想实现一个算法来描述一个适当的数学(即纯)函数,但似乎适合带有​​变异等的命令式编程风格,那么你不应该在 IO 中实现它单子。您可以通过选择合适的数据结构在普通的纯 Haskell98 中实现它,或者使用 the ST monad 来实现例如数组更新具有与命令式语言相同的性能。

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

  • 如果 有办法做到这一点呢?

  • 如果其他人可以使用它怎么办?

您是否愿意坐下来仔细检查您用来检查每个库和程序的来源以确保它们是安全的?

如果你是,那么 Haskell 可能不适合你 - 我建议你看看像 Standard ML or OCaml...

这样的语言

你还在吗?

好吧,您可以使用另一种方法:

  • 使用普通的普通函数从 effect-centric 代码中抽象出常规 Haskell 代码。

这样做是可能的,因为 函数是 first-class values in Haskell - 特别是,函数可以用作参数 例如:

fmap  :: Functor f => (a -> b) -> f a -> f b
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c

随着抽象能力的提高,您将把越来越多的工作委派给常规 Haskell 代码(以函数的形式),只有一小部分定义会受到影响(包括 main :: IO ()).它需要一些额外的努力才能开始,但是 long-term 回报是可观的...