从文件加载纯全局变量

hel*_*ami 12 haskell global-variables

我有一个包含一些数据的文件.这些数据永远不会改变,我想让它在IO monad之外可用.我怎样才能做到这一点?

示例(请注意,这只是一个示例,我的数据不可计算):

primes.txt:

2 3 5 7 13

code.hs:

primes :: [Int]
primes = map read . words . unsafePerformIO . readFile $ "primes.txt"
Run Code Online (Sandbox Code Playgroud)

这是"合法"使用unsafePerformIO吗?还有替代品吗?

dfl*_*str 20

您可以使用TemplateHaskell在编译时读入文件.然后,文件的数据将作为实际字符串存储在程序中.

在一个模块中(Text/Literal/TH.hs在此示例中),定义:

module Text.Literal.TH where

import Language.Haskell.TH
import Language.Haskell.TH.Quote

literally :: String -> Q Exp
literally = return . LitE . StringL

lit :: QuasiQuoter
lit = QuasiQuoter { quoteExp = literally }

litFile :: QuasiQuoter
litFile = quoteFile lit
Run Code Online (Sandbox Code Playgroud)

在您的模块中,您可以执行以下操作:

{-# LANGUAGE QuasiQuotes #-}
module MyModule where

import Text.Literal.TH (litFile)

primes :: [Int]
primes = map read . words $ [litFile|primes.txt|]
Run Code Online (Sandbox Code Playgroud)

编译程序时,GHC将打开primes.txt文件并将其内容插入到[litFile|primes.txt|]部件所在的位置.

  • 如果有问题的数据应该_never_更改,那么我会建议这是最好的答案. (4认同)

Ben*_*Ben 6

unsafePerformIO以这种方式使用并不是很好.

声明primes :: [Int]说这primes是一个数字列表.一个特定的数字列表,不依赖于任何东西.

实际上,当定义恰好被评估时,它取决于文件"primes.txt"的状态.有人可以更改此文件以更改primes看似具有的值,根据其类型,该值不可能.

在假设优化的情况下,决定primes应该按需重新计算而不是完全存储在内存中(毕竟,它的类型表示我们每次重新计算时都会得到相同的东西),primes甚至可能看起来有两种不同在单次运行程序期间的值.这是unsafePerformIO用于欺骗编译器的问题.

实际上,上述所有情况都可能不是问题.

但理论上正确的做法是不要制作primes全局常数(因为它不是常数).相反,你进行需要在其上进行参数化的计算(即primes作为参数),并在外部IO程序中读取文件,然后通过传递IO从文件中提取的程序的纯值来调用纯计算.你可以获得两全其美的感觉; 您不必欺骗编译器,也不必将整个程序放入IO.您可以使用诸如Reader monad之类的构造,以避免primes在任何地方手动传递,如果这有帮助的话.

所以你可以使用它,unsafePerformIO如果你想继续它.这在理论上是错误的,但不太可能在实践中引起问题.

或者你可以重构你的程序以反映真实情况.

或者,如果primes真的是一个全局常量,并且您只是不希望在程序源中包含大量数据,则可以使用dflemstr演示的TemplateHaskell.