Joh*_*ith 6 haskell functional-programming
我正在Haskell的一个项目上工作,我需要一个全局变量.目前我这样做:
funcs :: Map.Map String Double
funcs = Map.empty
eliminate :: Maybe a -> a
eliminate (Just a) = a
insert :: String -> Double -> Map.Map String Double -> Map.Map String Double
insert key value cache = Map.insert key value cache
f = do
let aux = insert "aaa" 1 funcs
let funcs = aux
.........
g = do
if (Map.lookup "aaa" funcs) == Nothing then error "not defined" else putStr "ok"
Run Code Online (Sandbox Code Playgroud)
问题是总是g函数抛出错误.你知道我怎样才能模拟全局变量?
val*_*man 19
有了let funcs = aux你只给funcs在范围的新的绑定f功能,这意味着funcs你参照g的是一个在全球范围内-这是定义为一个Map.empty.在运行时无法更改全局或其他纯值.但是,可以使用可变引用.最好是在当地,但也在全球范围内有一些不安全的hackery.
是否真的有必要使用全局变量?如果您在整个程序中没有使用全局,则可能需要将所有使用它的计算包装在Statemonad中:
import Control.Monad.State
import qualified Data.Map as Map
funcs :: Map.Map String Double
funcs = Map.empty
f :: String -> Double -> State (Map.Map String Double) ()
f str d = do
funcs <- get
put (Map.insert str d funcs)
g :: State (Map.Map String Double) String
g = do
funcs <- get
if (Map.lookup "aaa" funcs) == Nothing then return "not defined" else return "ok"
main = putStrLn $ flip evalState funcs $ do {f "aaa" 1; g}
Run Code Online (Sandbox Code Playgroud)
通过这种方式保持您的状态受限,可以更容易地跟踪您的程序增长; 你总是知道哪些计算可能会改变你的状态,因为它的类型清楚地表明了这一点.
另一方面,如果你出于某种原因绝对需要全局变量,那么使用IORefs和一个众所周知但相当丑陋的技巧unsafePerformIO:
import Data.IORef
import System.IO.Unsafe
import qualified Data.Map as Map
{-# NOINLINE funcs #-}
funcs :: IORef (Map.Map String Double)
funcs = unsafePerformIO $ newIORef Map.empty
f :: String -> Double -> IO ()
f str d = atomicModifyIORef funcs (\m -> (Map.insert str d m, ()))
g :: IO ()
g = do
fs <- readIORef funcs
if (Map.lookup "aaa" fs) == Nothing then error "not defined" else putStrLn "ok"
main = do
f "aaa" 1
g
Run Code Online (Sandbox Code Playgroud)
这个技巧创建了一个全局IORef,可以在IOmonad中读取和更新.这意味着执行IO的任何计算都可能会改变全局值,这会给您带来全局状态的所有令人头疼的问题.除此之外,这个技巧也非常hacky,只有GHC中的实现细节才有效(例如参见该{-# NOINLINE funcs #-}部分).
如果你决定使用这个hack(我真的建议反对),请记住你绝对不能将它与多态值一起使用.为了说明原因:
import Data.IORef
import System.IO.Unsafe
{-# NOINLINE danger #-}
danger :: IORef a
danger = unsafePerformIO $ newIORef undefined
coerce :: a -> IO b
coerce x = do
writeIORef danger x
readIORef danger
main = do
x <- coerce (0 :: Integer) :: IO (Double, String) -- boom!
print x
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这个技巧可以与多态一起使用来编写一个函数,将任何类型重新解释为任何其他类型,这显然会破坏类型安全性,因此可能导致程序出现段错误(最好).
总之,请考虑使用Statemonad而不是全局变量; 不要轻易转向全局变量.