Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simulate global variable

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?

like image 510
John Smith Avatar asked May 29 '13 10:05

John Smith


People also ask

What are alternatives to global variables?

In a scenario, where you need one central global point of access across the codebase, singletons are a good alternative for global variables. We can call a singleton as a lazily initialized global class which is useful when you have large objects — memory allocation can be deferred till when it's actually needed.

How do you create a global variable?

Normally, when you create a variable inside a function, that variable is local, and can only be used inside that function. To create a global variable inside a function, you can use the global keyword.

Can we declare global variable as auto?

The keyword auto can be used to explicitly create these variables, but isn't necessary since auto is the default. A global variable is a variable that is defined outside all functions and available to all functions.

Can you make global variables in Java?

There are no global variables in Java, but there are global classes with public fields. You can use static import feature of java 5 to make it look almost like global variables.


2 Answers

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.

like image 63
valderman Avatar answered Nov 11 '22 20:11

valderman


Functional programming doesn't have states or global variables, every function should be able to run separetely from everyone else. There are some tricks:

  1. Write and read state to file

http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:readFile

  1. Use state Monads ( http://en.wikibooks.org/wiki/Haskell/Understanding_monads/State )

    import Data.IORef
    
    type Counter = Int -> IO Int
    
    makeCounter :: IO Counter
    makeCounter = do
        r <- newIORef 0
       return (\i -> do modifyIORef r (+i)
                        readIORef r)
    
    testCounter :: Counter -> IO ()
    testCounter counter = do
      b <- counter 1
      c <- counter 1
      d <- counter 1
      print [b,c,d]
    
    main = do
      counter <- makeCounter
      testCounter counter
      testCounter counter
    
like image 20
Igor S. Avatar answered Nov 11 '22 20:11

Igor S.