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|]
部件所在的位置.
unsafePerformIO
以这种方式使用并不是很好.
声明primes :: [Int]
说这primes
是一个数字列表.一个特定的数字列表,不依赖于任何东西.
实际上,当定义恰好被评估时,它取决于文件"primes.txt"的状态.有人可以更改此文件以更改primes
看似具有的值,根据其类型,该值不可能.
在假设优化的情况下,决定primes
应该按需重新计算而不是完全存储在内存中(毕竟,它的类型表示我们每次重新计算时都会得到相同的东西),primes
甚至可能看起来有两种不同在单次运行程序期间的值.这是unsafePerformIO
用于欺骗编译器的问题.
实际上,上述所有情况都可能不是问题.
但理论上正确的做法是不要制作primes
全局常数(因为它不是常数).相反,你进行需要在其上进行参数化的计算(即primes
作为参数),并在外部IO
程序中读取文件,然后通过传递IO
从文件中提取的程序的纯值来调用纯计算.你可以获得两全其美的感觉; 您不必欺骗编译器,也不必将整个程序放入IO
.您可以使用诸如Reader monad之类的构造,以避免primes
在任何地方手动传递,如果这有帮助的话.
所以你可以使用它,unsafePerformIO
如果你想继续它.这在理论上是错误的,但不太可能在实践中引起问题.
或者你可以重构你的程序以反映真实情况.
或者,如果primes
真的是一个全局常量,并且您只是不希望在程序源中包含大量数据,则可以使用dflemstr演示的TemplateHaskell.