I've been learning Haskell for a few weeks after coming from a C# background. I've written a function to return a number of unique Ints from a specified range:
module MrKWatkins.Random (
uniqueRandomInts
) where
import System.Random
import qualified Data.IntSet as I
uniqueRandomInts :: RandomGen g => (Int, Int) -> Int -> g -> ([Int], g)
uniqueRandomInts range@(min, max) n rng
| n < 0 = error "n must be positive"
| n > max - min + 1 = error "n is too large for range"
| otherwise = recursion n I.empty ([], rng)
where
recursion n used (xs, rng)
| n == 0 = (xs, rng)
| I.member x used = recursion n used (xs, rng')
| otherwise = recursion (n-1) (I.insert x used) (x:xs, rng')
where
(x, rng') = randomR range rng
Then to produce 5 numbers in the (inclusive) range 1 -> 10 the call would be something like:
uniqueRandomInts (1, 10) 5 $ mkStdGen 42
Firstly I have a few specific questions:
- I've prefixed my module name with MrKWatkins
MrKWatkinsto distinguish it as my module. (Muchmuch as I would with a namespace in C#). Is this common practice, or would I be better with a single word for the module name that is more descriptive? - I've done some pre-condition checking on the parameters, throwing exceptions if they're not satisfied. Is this common practice in Haskell, and is this the best way to do it?
- I've used a nested function for the recursion. Would I be better off separating this out into a top level function that I don't export from the module?
- I've reused some variable names in the recursion function, hiding the ones from the parent scope. Should I use different names instead?
On top of those if there is any advice on style, better ways to implement the above or just general advice would be greatly appreciated.