So one of the things that happens is heavier reliance on recursion, the values passed to the function can change like a variable, but they're still parameters. Which leads to encapsulating values in a state monad, and then passing the monadic values around.
What is a monad? That is where most people get very confused. Mathematically a monad is different than how it is implemented in Haskell! In Haskell a monad is really just a data type pointer. So you have something like IO Integer and it's a pointer to an Integer. Unfortunately the IO monad has no good constructor, you need to use IOArray, IORef, TVar or some other monad to create a variable. For now let's stick to using IORef since it's intended for this.
module Main where
import Data.IORef
main = do
-- a new variable called var
var <- newIORef (1::Integer)
-- the contents of var
a <- readIORef var
-- add one to var
modifyIORef var (+1)
-- the contents of var
b <- readIORef var
-- set var to 3
writeIORef var 3
-- the contents of var
c <- readIORef var
putStrLn $ show a
putStrLn $ show b
putStrLn $ show c
This is how variables are done in Haskell, with the following caveat: in general IORef isn't thread safe. So you might rather use something like:
module Variables (Variable, newVar, setVar, getVar, modVar, showVar) where
import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
type Variable a = TVar a
-- initialize a new variable
newVar :: a -> IO (Variable a)
newVar value = atomically $ newTVar value
-- get the value of a variable
getVar :: Variable a -> IO a
getVar pntr = atomically $ do
value <- readTVar pntr
return value
-- set the value of a variable
setVar :: Variable a -> a -> IO a
setVar pntr value = atomically $ do
writeTVar pntr value
return value
-- apply a function to a variable
modVar :: Variable a -> (a -> a) -> IO a
modVar pntr fn = atomically $ do
value1 <- readTVar pntr
let value2 = fn value1
writeTVar pntr value2
return value2
-- a show function for a variable (for GHCI)
showVar :: (Show a) => Variable a -> IO ()
showVar pntr = do
value <- atomically $ readTVar pntr
putStrLn $ show value
This is thread safe on top of TVar. Variables.hs
So using this we'd have:
module Main where
import Variables
main = do
-- a new variable called var
var <- newVar (1::Integer)
-- the contents of var
a <- getVar var
-- add one to var
modVar var (+1)
-- the contents of var
b <- getVar var
-- set var to 3
setVar var 3
-- the contents of var
c <- getVar var
putStrLn $ show a
putStrLn $ show b
putStrLn $ show c
Which is almost the same as using IORef, but thread safe.
No comments:
Post a Comment