模拟全局变量 [英] Simulate global variable

查看:97
本文介绍了模拟全局变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一个Haskell的项目,我需要一个全局变量。目前我正在做这件事:

  funcs :: Map.Map字符串Double 
funcs = Map.empty

elimination ::也许a - > a
elimination(Just a)= a

insert :: String - >双 - > Map.Map String Double - > Map.Map String Double
插入键值cache = Map.insert键值缓存

f = do

let aux = insertaaa1 funcs
让funcs = aux
.........


g = do
if(Map.lookupaaafuncs)== Nothing错误未定义else putStrok

问题是总是出现g函数抛出错误。你知道我怎样才能模拟一个全局变量? c $ c> funcs f 函数的一个新绑定,这意味着 funcs 你指的是 g 是全局范围中的一个 - 定义为 Map.empty 。在运行时不可能更改全局值或其他值。但是,可以使用可变引用。最好在本地,但在全球范围内也有一些不安全的骇客。

是否真的有必要使用全局变量?如果您在整个程序中没有使用全局,则可能需要将使用它的所有计算包装在 State monad中。

  import Control.Monad.State 
将限定的Data.Map导入为Map

funcs :: Map.Map String Double
funcs = Map.empty

f :: String - >双 - > 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.lookupaaafuncs)== Nothing然后返回not definedelse返回 OK

main = putStrLn $ flip evalState funcs $ do {faaa1; g}

以这种方式保持您的状态受到限制,可以更轻松地跟踪程序的增长;你总是知道哪些计算可能会改变你的状态,因为它的类型清楚地表明了它。



另一方面,如果你出于某种原因绝对需要全局变量,使用 IORef s和 unsafePerformIO


$ b一个众所周知但相当难看的技巧$ b

  import Data.IORef 
import System.IO.Unsafe
将合格的Data.Map导入为Map

{ - # NOINLINE funcs# - }
funcs :: IORef(Map.Map String Double)
funcs = unsafePerformIO $ newIORef Map.empty
$ b $ :: String - >双 - > IO()
f str d = atomicModifyIORef funcs(\ m - >(Map.insert str dm,()))
$ bg :: IO()
g = do
fs< - readIORef funcs
if(Map.lookupaaafs)== Nothing然后错误not definedelse putStrLnok

main = do
faaa1
g

这个技巧创建了一个全局 IORef ,它可以在 IO monad中读取和更新。这意味着任何执行IO的计算都可能会改变您的全局值,这会给您带来令人头痛的全局状态。除此之外,这个技巧也很棘手,只能用于GHC的实现细节(例如,参见 { - #NOINLINE funcs# - } 部分)。



如果您决定使用这种黑客(我认为真的不推荐),请记住,您可以绝对不是使用它与多态值。为了说明原因:

  import Data.IORef 
import System.IO.Unsafe

{ - #NOINLINE危险# - }
危险:: IORef a
危险= unsafePerformIO $ newIORef未定义

coerce :: a - > IO b
coerce x = do
writeIORef危险x
readIORef危险

main = do
x< - coerce(0 :: Integer):: IO(Double,String) - 繁荣!
print x

正如您所看到的,这个技巧可以与多态一起使用来编写一个函数,它会将任何类型重新解释为任何其他类型,这明显会破坏类型安全性,因此可能会导致您的程序发生段错误(最好)。



总而言之, do 考虑使用 State monad而不是全局变量; 不要轻轻转向全局变量。


I'm working on a project in Haskell and I need a global variable. Currently I'm doing this:

 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"

The problem is that always g function throw error. Do you know how can I simulate a global variable?

解决方案

With let funcs = aux you're only giving funcs a new binding in the scope of the f function, which means that the funcs you're referring to in g is the one in the global scope - the one that's defined as Map.empty. It is not possible to change pure values, global or otherwise, at runtime. It is, however, possible to use mutable references. Preferrably locally, but also globally with a bit of unsafe hackery.

Is it really necessary to use a global variable though? If you're not using your global throughout your entire program, you may want to wrap all the computations that use it in a State monad instead:

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}

Keeping your state constrained in this way makes it easier to keep track of your program as it grows; you always know which computations may alter your state as it's clearly indicated by its type.

If, on the other hand, you for some reason absolutely need global variables, there is a well-known but fairly ugly trick using IORefs and 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

This trick creates a global IORef, which can be read and updated inside the IO monad. This means that any computation that performs IO may change the value of your global, which gives you all the wonderful headaches of global state. Aside from that, this trick is also extremely hacky and only works because of implementation details in GHC (see the {-# NOINLINE funcs #-} part, for instance).

If you decide to use this hack (which I really recommend against), keep in mind that you can absolutely not use it with polymorphic values. To illustrate why:

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

As you can see, this trick can be used together with polymorphism to write a function that reinterprets any type as any other type, which obviously breaks type safety and so may cause your programs to segfault (at best).

In summary, do consider using the State monad instead of global variables; do not turn to global variables lightly.

这篇关于模拟全局变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆