I have a function called generateUID
, which is external C function connected via FFI.
This function generates new unique id for each call, but I need one uid for entire program. If I were using C/C++, I would've made a function like this one.
int getUID() {
static int uid = generateUID();
return uid;
}
So that I can use this like this
int foo() { return getUID() + 1; }
int bar() { return getUID() + 2; }
int main() {
printf("%d\n", foo() + bar();
}
In haskell, I used function like this
getUID :: IO Int
getUID = generateUID -- this is attached to C lib with FFI
foo = (+1) <$> getUID
bar = (+2) <$> getUID
main = (+) <$> foo <*> bar >>= print
However, getUID
is called twice if I use this code. The only solution that I know of is to merge them into one do
notation, but in my real code, foo
and bar
are reused too frequently to be merged to other function.
How can I make my haskell version getUID
call generateUID
only once?
You can use the following trick, adapted from this method for defining global mutable variables. For your application, you don't need a mutable variable, but by using unsafePerformIO
in a top-level definition, you can force the IO action generateUID
to be called only once and its return value memoized for future calls:
getUID :: Int
{-# NOINLINE getUID #-}
getUID = unsafePerformIO generateUID
For example, the following complete example will re-use the same random number generated by the first getUID
call for any future calls:
{-# LANGUAGE ForeignFunctionInterface #-}
module Uid where
import System.IO.Unsafe
foreign import ccall "stdlib.h random" generateUID :: IO Int
getUID :: Int
{-# NOINLINE getUID #-}
getUID = unsafePerformIO generateUID
foo = getUID + 1
bar = getUID + 2
main = print $ (foo, bar)
This has the advantage that getUID
is just a pure value, so you can use it outside of IO
monad, if needed.
Admittedly, many people consider this a horrible hack. Typically, a cleaner alternative is to run the program in a monad with a Reader
component that includes the global ID value. Usually you'll be running big chunks of any non-trivial program in some kind of monad anyway (e.g., above, you seemed willing to run foo
and bar
in the IO
monad), so this is rarely much of an issue. Something like:
{-# LANGUAGE ForeignFunctionInterface #-}
module Uid where
import Control.Monad.Reader
data R = R { globalUid :: Int }
type M = ReaderT R IO
foreign import ccall "stdlib.h random" generateUID :: IO Int
getUID :: M Int
getUID = asks globalUid
foo = (+1) <$> getUID
bar = (+2) <$> getUID
withUid :: M a -> IO a
withUid act = do
uid <- generateUID
runReaderT act (R uid)
main = withUid $ do
liftIO . print =<< (,) <$> foo <*> bar
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With