[Haskell] randomgen for foreign random functions

Glynn Clements glynn.clements at virgin.net
Fri Jun 25 16:14:13 EDT 2004


Hal Daume III wrote:

> Say I have a foreign function:
> 
> foreign import ccall "statistical_c.h gengam"     c_gengam :: CDouble -> CDouble -> IO CDouble
> 
> that returns a random value parameterized by the two CDoubles.  clearly 
> this must happen in IO since the return value will be different each time, 
> and some global state stuff is getting modified on the C side to 
> facilitate future random # generation.
> 
> However, I would like to wrap this into an essentially pure function, ala 
> the Random class.  basically, i want to wrap this up as something like:
> 
> gengam :: RandomGen g => g -> Double -> Double -> (g, Double)
> 
> analogously to the normal random # generation stuff.
> 
> is there a safe way to do this?  one option, of course, is just to 
> unsafePerformIO it and {-# NOINLINE #-} the call, but that is rather, uhm, 
> unsafe, I believe.  it seems to me that something like this should be 
> possible.
> 
> Any thoughts?

What were you planning on doing with the "g" parameter?

In order to (safely) have the above type, the behaviour of gengam
would have to be such that, if called repeatedly with the same
arguments (including the g), it would return the same (g, Double)
tuple each time.

AFAICT, unless you can get at c_gengam's state, you can't (safely) get
the Haskell version out of the IO monad. OTOH, if you have a means to
set the seed, you could combine the two into a pure function, and thus
provide a pure (non-IO) Haskell interface. E.g. if you had:

	void seedgam(int seed)
	{
		srand(seed);
	}

	double gengam(double lo, double hi)
	{
		return lo + rand() * (hi - lo) / RAND_MAX;
	}

you could either combine them in C:

	double do_gengam(int seed, double lo, double hi)
	{
		seedgam(seed);
		return gengam(lo, hi);
	}

import that as a pure function:

	foreign import ccall "statistical_c.h do_gengam" c_do_gengam :: CInt -> CDouble -> CDouble -> CDouble

and interface to it as:

	gengam g lo hi = (g', c_gengam seed lo hi))
		where	(seed, g') = next g

or import them individually as:

	foreign import ccall "statistical_c.h seedgam" c_seedgam :: CInt -> IO ()
	foreign import ccall "statistical_c.h gengam" c_gengam :: CDouble -> CDouble -> IO CDouble

and (safely) use unsafePerformIO, e.g.

	gengam g lo hi = (g', x)
		where	x = unsafePerformIO $ do
				seedgam seed
				x <- c_gengam lo hi
			(seed, g') = next g

[Although, in the second approach, I presume that you would need
addtional code for multi-threaded use.]

-- 
Glynn Clements <glynn.clements at virgin.net>


More information about the Haskell mailing list