[Haskell-cafe] Generating random enums
What is the minimum I need to do to get this function to generate a three direction tuple? Michael = import System.Random import Data.Ord data Dir = North | South | East | West deriving (Show, Read, Eq, Enum, Ord, Bounded) threeDirs :: StdGen - (Dir,Dir,Dir) threeDirs gen = let (firstDir, newGen) = random gen (secondDir, newGen') = random newGen (thirdDir, newGen'') = random newGen' in (firstDir, secondDir, thirdDir) = GHCi, version 6.10.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. Prelude :l dir.hs [1 of 1] Compiling Main ( dir.hs, interpreted ) dir.hs:15:29: No instance for (Random Dir) arising from a use of `random' at dir.hs:15:29-42 Possible fix: add an instance declaration for (Random Dir) In the expression: random newGen' In a pattern binding: (thirdDir, newGen'') = random newGen' In the expression: let (firstDir, newGen) = random gen (secondDir, newGen') = random newGen (thirdDir, newGen'') = random newGen' in (firstDir, secondDir, thirdDir) Failed, modules loaded: none. Prelude ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
I didn't try to compile this: import Control.Arrow (first) import System.Random (Random(..)) instance Random Dir where randomR (lo, hi) gen = first fromEnum (randomR (toEnum lo) (toEnum hi) gen) random gen = randomR (minBound, maxBound) But something along those lines should help you I think. -Ross On Oct 16, 2009, at 3:36 PM, michael rice wrote: What is the minimum I need to do to get this function to generate a three direction tuple? Michael = import System.Random import Data.Ord data Dir = North | South | East | West deriving (Show, Read, Eq, Enum, Ord, Bounded) threeDirs :: StdGen - (Dir,Dir,Dir) threeDirs gen = let (firstDir, newGen) = random gen (secondDir, newGen') = random newGen (thirdDir, newGen'') = random newGen' in (firstDir, secondDir, thirdDir) = GHCi, version 6.10.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. Prelude :l dir.hs [1 of 1] Compiling Main ( dir.hs, interpreted ) dir.hs:15:29: No instance for (Random Dir) arising from a use of `random' at dir.hs:15:29-42 Possible fix: add an instance declaration for (Random Dir) In the expression: random newGen' In a pattern binding: (thirdDir, newGen'') = random newGen' In the expression: let (firstDir, newGen) = random gen (secondDir, newGen') = random newGen (thirdDir, newGen'') = random newGen' in (firstDir, secondDir, thirdDir) Failed, modules loaded: none. Prelude ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
OK, I think what you're saying is to work with (random) integers and use fromEnum and toEnum to get corresponding DayOfWeek. But I get this when I try to use toEnum: [mich...@localhost ~]$ ghci dow GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. [1 of 1] Compiling Main ( dow.hs, interpreted ) Ok, modules loaded: Main. *Main fromEnum Wednesday Loading package old-locale-1.0.0.1 ... linking ... done. Loading package old-time-1.0.0.1 ... linking ... done. Loading package random-1.0.0.1 ... linking ... done. 2 *Main toEnum 2 *** Exception: Prelude.Enum.().toEnum: bad argument *Main Michael --- On Fri, 5/1/09, John Van Enk vane...@gmail.com wrote: From: John Van Enk vane...@gmail.com Subject: Re: [Haskell-cafe] Generating random enums To: michael rice nowg...@yahoo.com Cc: haskell-cafe@haskell.org Date: Friday, May 1, 2009, 12:36 PM When you derive Enum, you get fromEnum and toEnum for free. You don't need your dow2Int stuff or int2Dow. Replace those with fromEnum and toEnum respectively. /jve On Fri, May 1, 2009 at 12:26 PM, michael rice nowg...@yahoo.com wrote: I'm using the code below to generate random days of the week [Monday..Sunday]. Is there a better/shorter way to do this? Michael == [mich...@localhost ~]$ ghci dow GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. [1 of 1] Compiling Main ( dow.hs, interpreted ) Ok, modules loaded: Main. *Main random (mkStdGen 100) :: (DayOfWeek, StdGen) Loading package old-locale-1.0.0.1 ... linking ... done. Loading package old-time-1.0.0.1 ... linking ... done. Loading package random-1.0.0.1 ... linking ... done. (Friday,4041414 40692) *Main random (mkStdGen 123) :: (DayOfWeek, StdGen) (Tuesday,4961736 40692) *Main == import System.Random data DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Show, Read, Eq, Enum, Ord, Bounded) instance Random DayOfWeek where randomR (a,b) g = case (randomIvalInteger (toInteger (dow2Int a), toInteger (dow2Int b)) g) of (x, g) - (int2Dow x, g) where dow2Int Monday = 0 dow2Int Tuesday = 1 dow2Int Wednesday = 2 dow2Int Thursday = 3 dow2Int Friday = 4 dow2Int Saturday = 5 dow2Int Sunday = 6 int2Dow 0 = Monday int2Dow 1 = Tuesday int2Dow 2 = Wednesday int2Dow 3 = Thursday int2Dow 4 = Friday int2Dow 5 = Saturday int2Dow 6 = Sunday random g = randomR (minBound,maxBound) g randomIvalInteger :: (RandomGen g, Num a) = (Integer, Integer) - g - (a, g) randomIvalInteger (l,h) rng | l h = randomIvalInteger (h,l) rng | otherwise = case (f n 1 rng) of (v, rng') - (fromInteger (l + v `mod` k), rng') where k = h - l + 1 b = 2147483561 n = iLogBase b k f 0 acc g = (acc, g) f n acc g = let (x,g') = next g in f (n-1) (fromIntegral x + acc * b) g' iLogBase :: Integer - Integer - Integer iLogBase b i = if i b then 1 else 1 + iLogBase b (i `div` b) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe -- /jve ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
OK, I think what you're saying is to work with (random) integers and use fromEnum and toEnum to get corresponding DayOfWeek. But I get this when I try to use toEnum: *Main toEnum 2 ghci does not know what type of enum you want to create from the number 2. Try: toEnum 2 :: DayOfWeek That said, I would expect toEnum 2 to give an error like: 'Ambiguous type variable `a''. So I am not sure why your error message says: '** Exception: Prelude.Enum.().toEnum: bad argument' ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
I intuited that that's what the problem was. Thanks, Michael --- On Sat, 5/2/09, Rahul Kapoor r...@trie.org wrote: From: Rahul Kapoor r...@trie.org Subject: Re: [Haskell-cafe] Generating random enums To: michael rice nowg...@yahoo.com Cc: John Van Enk vane...@gmail.com, haskell-cafe@haskell.org Date: Saturday, May 2, 2009, 2:00 PM OK, I think what you're saying is to work with (random) integers and use fromEnum and toEnum to get corresponding DayOfWeek. But I get this when I try to use toEnum: *Main toEnum 2 ghci does not know what type of enum you want to create from the number 2. Try: toEnum 2 :: DayOfWeek That said, I would expect toEnum 2 to give an error like: 'Ambiguous type variable `a''. So I am not sure why your error message says: '** Exception: Prelude.Enum.().toEnum: bad argument' ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
Am Samstag 02 Mai 2009 20:00:54 schrieb Rahul Kapoor: OK, I think what you're saying is to work with (random) integers and use fromEnum and toEnum to get corresponding DayOfWeek. But I get this when I try to use toEnum: *Main toEnum 2 ghci does not know what type of enum you want to create from the number 2. Try: toEnum 2 :: DayOfWeek That said, I would expect toEnum 2 to give an error like: 'Ambiguous type variable `a''. So I am not sure why your error message says: '** Exception: Prelude.Enum.().toEnum: bad argument' Defaulting. If ghci doesn't know which type (let's call it a) is demanded, it looks at the constraints (here Enum a) and tries to choose a type from its default list that satisfies the constraints. AFAIK, the default list is (), Integer, Double. () satisfies the constraint, so ghci chooses that. fromEnum () is 0, so 0 is the only acceptable argument for toEnum :: Int - (). It's a two-edged sword, if ghci didn't default, it would have to issue a *lot* of ambiguous type messages and you would have to give a type signature to most expressions you type at the prompt, with the defaulting, you get pretty unintuitive error messages when it does the wrong thing. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
Rahul Kapoor r...@trie.org writes: *Main toEnum 2 ghci does not know what type of enum you want to create from the number 2. Try: toEnum 2 :: DayOfWeek That said, I would expect toEnum 2 to give an error like: 'Ambiguous type variable `a''. So I am not sure why your error message says: '** Exception: Prelude.Enum.().toEnum: bad argument' Wild guess: The dread monomorphism restriction uses () as a reasonable type, and it only has one element. Does 'toEnum 0' give '()' as the result? (I couldn't reproduce it with my version of GHCi - I get the ambigous type error message) -k -- If I haven't seen further, it is by standing in the footprints of giants ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
Am Samstag 02 Mai 2009 21:22:26 schrieb Ketil Malde: Rahul Kapoor r...@trie.org writes: *Main toEnum 2 ghci does not know what type of enum you want to create from the number 2. Try: toEnum 2 :: DayOfWeek That said, I would expect toEnum 2 to give an error like: 'Ambiguous type variable `a''. So I am not sure why your error message says: '** Exception: Prelude.Enum.().toEnum: bad argument' Wild guess: The dread monomorphism restriction uses () as a reasonable type, and it only has one element. Does 'toEnum 0' give '()' as the result? Actually, it seems to be the other way round here: Prelude toEnum 2 interactive:1:0: Ambiguous type variable `a' in the constraint: `Enum a' arising from a use of `toEnum' at interactive:1:0-7 Probable fix: add a type signature that fixes these type variable(s) Prelude :set -fno-monomorphism-restriction no location info: Warning: -fno-monomorphism-restriction is deprecated: use -XNoMonomorphismRestriction or pragma {-# LANGUAGE NoMonomorphismRestriction#-} instead Prelude toEnum 2 *** Exception: Prelude.Enum.().toEnum: bad argument (I couldn't reproduce it with my version of GHCi - I get the ambigous type error message) -k ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Generating random enums
I'm using the code below to generate random days of the week [Monday..Sunday]. Is there a better/shorter way to do this? Michael == [mich...@localhost ~]$ ghci dow GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. [1 of 1] Compiling Main ( dow.hs, interpreted ) Ok, modules loaded: Main. *Main random (mkStdGen 100) :: (DayOfWeek, StdGen) Loading package old-locale-1.0.0.1 ... linking ... done. Loading package old-time-1.0.0.1 ... linking ... done. Loading package random-1.0.0.1 ... linking ... done. (Friday,4041414 40692) *Main random (mkStdGen 123) :: (DayOfWeek, StdGen) (Tuesday,4961736 40692) *Main == import System.Random data DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Show, Read, Eq, Enum, Ord, Bounded) instance Random DayOfWeek where randomR (a,b) g = case (randomIvalInteger (toInteger (dow2Int a), toInteger (dow2Int b)) g) of (x, g) - (int2Dow x, g) where dow2Int Monday = 0 dow2Int Tuesday = 1 dow2Int Wednesday = 2 dow2Int Thursday = 3 dow2Int Friday = 4 dow2Int Saturday = 5 dow2Int Sunday = 6 int2Dow 0 = Monday int2Dow 1 = Tuesday int2Dow 2 = Wednesday int2Dow 3 = Thursday int2Dow 4 = Friday int2Dow 5 = Saturday int2Dow 6 = Sunday random g = randomR (minBound,maxBound) g randomIvalInteger :: (RandomGen g, Num a) = (Integer, Integer) - g - (a, g) randomIvalInteger (l,h) rng | l h = randomIvalInteger (h,l) rng | otherwise = case (f n 1 rng) of (v, rng') - (fromInteger (l + v `mod` k), rng') where k = h - l + 1 b = 2147483561 n = iLogBase b k f 0 acc g = (acc, g) f n acc g = let (x,g') = next g in f (n-1) (fromIntegral x + acc * b) g' iLogBase :: Integer - Integer - Integer iLogBase b i = if i b then 1 else 1 + iLogBase b (i `div` b) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
When you derive Enum, you get fromEnum and toEnum for free. You don't need your dow2Int stuff or int2Dow. Replace those with fromEnum and toEnum respectively. /jve On Fri, May 1, 2009 at 12:26 PM, michael rice nowg...@yahoo.com wrote: I'm using the code below to generate random days of the week [Monday..Sunday]. Is there a better/shorter way to do this? Michael == [mich...@localhost ~]$ ghci dow GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. [1 of 1] Compiling Main ( dow.hs, interpreted ) Ok, modules loaded: Main. *Main random (mkStdGen 100) :: (DayOfWeek, StdGen) Loading package old-locale-1.0.0.1 ... linking ... done. Loading package old-time-1.0.0.1 ... linking ... done. Loading package random-1.0.0.1 ... linking ... done. (Friday,4041414 40692) *Main random (mkStdGen 123) :: (DayOfWeek, StdGen) (Tuesday,4961736 40692) *Main == import System.Random data DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Show, Read, Eq, Enum, Ord, Bounded) instance Random DayOfWeek where randomR (a,b) g = case (randomIvalInteger (toInteger (dow2Int a), toInteger (dow2Int b)) g) of (x, g) - (int2Dow x, g) where dow2Int Monday= 0 dow2Int Tuesday = 1 dow2Int Wednesday = 2 dow2Int Thursday = 3 dow2Int Friday= 4 dow2Int Saturday = 5 dow2Int Sunday= 6 int2Dow 0= Monday int2Dow 1= Tuesday int2Dow 2= Wednesday int2Dow 3= Thursday int2Dow 4= Friday int2Dow 5= Saturday int2Dow 6= Sunday random g = randomR (minBound,maxBound) g randomIvalInteger :: (RandomGen g, Num a) = (Integer, Integer) - g - (a, g) randomIvalInteger (l,h) rng | l h = randomIvalInteger (h,l) rng | otherwise = case (f n 1 rng) of (v, rng') - (fromInteger (l + v `mod` k), rng') where k = h - l + 1 b = 2147483561 n = iLogBase b k f 0 acc g = (acc, g) f n acc g = let (x,g') = next g in f (n-1) (fromIntegral x + acc * b) g' iLogBase :: Integer - Integer - Integer iLogBase b i = if i b then 1 else 1 + iLogBase b (i `div` b) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe -- /jve ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
see http://www.mail-archive.com/haskell-cafe@haskell.org/msg38528.html for some ideas, particularly antoine latter's answer. 2009/5/1 michael rice nowg...@yahoo.com: I'm using the code below to generate random days of the week [Monday..Sunday]. Is there a better/shorter way to do this? Michael == [mich...@localhost ~]$ ghci dow GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. [1 of 1] Compiling Main ( dow.hs, interpreted ) Ok, modules loaded: Main. *Main random (mkStdGen 100) :: (DayOfWeek, StdGen) Loading package old-locale-1.0.0.1 ... linking ... done. Loading package old-time-1.0.0.1 ... linking ... done. Loading package random-1.0.0.1 ... linking ... done. (Friday,4041414 40692) *Main random (mkStdGen 123) :: (DayOfWeek, StdGen) (Tuesday,4961736 40692) *Main == import System.Random data DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Show, Read, Eq, Enum, Ord, Bounded) instance Random DayOfWeek where randomR (a,b) g = case (randomIvalInteger (toInteger (dow2Int a), toInteger (dow2Int b)) g) of (x, g) - (int2Dow x, g) where dow2Int Monday = 0 dow2Int Tuesday = 1 dow2Int Wednesday = 2 dow2Int Thursday = 3 dow2Int Friday = 4 dow2Int Saturday = 5 dow2Int Sunday = 6 int2Dow 0 = Monday int2Dow 1 = Tuesday int2Dow 2 = Wednesday int2Dow 3 = Thursday int2Dow 4 = Friday int2Dow 5 = Saturday int2Dow 6 = Sunday random g = randomR (minBound,maxBound) g randomIvalInteger :: (RandomGen g, Num a) = (Integer, Integer) - g - (a, g) randomIvalInteger (l,h) rng | l h = randomIvalInteger (h,l) rng | otherwise = case (f n 1 rng) of (v, rng') - (fromInteger (l + v `mod` k), rng') where k = h - l + 1 b = 2147483561 n = iLogBase b k f 0 acc g = (acc, g) f n acc g = let (x,g') = next g in f (n-1) (fromIntegral x + acc * b) g' iLogBase :: Integer - Integer - Integer iLogBase b i = if i b then 1 else 1 + iLogBase b (i `div` b) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
So... I must say I am rather pleased with the following code. It allows you to use any value of type Bounded and Enum as a member of Random, or Arbitrary, which means you can quickCheck properties on it as well. For quickchecking, the code below cheats by not defining the coarbitrary funciton, which I confess I don't really understand and never use. Even so, I can see myself using this in a number of places... Does it seem reasonable to add a ticket to get this added to http://hackage.haskell.org/packages/archive/QuickCheck/2.1.0.1/doc/html/Test-QuickCheck-Arbitrary.html perhaps modulo the definition of an appropriate coarbitrary function? thomas. thart...@patchwiki:~/haskell-learning/testingcat BoundedEnum.hs {-# LANGUAGE FlexibleInstances, UndecidableInstances, ScopedTypeVariables, OverlappingInstances #-} module Main where import Test.QuickCheck import System.Random class (Bounded a, Enum a) = BoundedEnum a instance (Bounded a, Enum a) = BoundedEnum a instance BoundedEnum a = Random a where random g = let min = fromEnum (minBound :: a) max = fromEnum (maxBound :: a) (i,g') = randomR (min,max) $ g in (toEnum i,g') randomR (low,high) g = let min = fromEnum low max = fromEnum high (i,g') = randomR (min,max) $ g in (toEnum i,g') instance BoundedEnum a = Arbitrary a where arbitrary = do let min = fromEnum (minBound :: a) max = fromEnum (maxBound :: a) i - arbitrary return . toEnum $ min + (i `mod` (max-min)) coarbitrary = undefined data DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Show, Read, Eq, Enum, Ord, Bounded) t :: IO DayOfWeek t = randomIO t2 :: IO Int t2 = randomIO pDayEqualsItself :: DayOfWeek - Bool pDayEqualsItself day = day == day -- a trivial property, just so we can show t3 = quickCheck pDayEqualsItself -- show what days are being tested t4 = verboseCheck pDayEqualsItself 2009/5/1 michael rice nowg...@yahoo.com: I'm using the code below to generate random days of the week [Monday..Sunday]. Is there a better/shorter way to do this? Michael == [mich...@localhost ~]$ ghci dow GHCi, version 6.10.1: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer ... linking ... done. Loading package base ... linking ... done. [1 of 1] Compiling Main ( dow.hs, interpreted ) Ok, modules loaded: Main. *Main random (mkStdGen 100) :: (DayOfWeek, StdGen) Loading package old-locale-1.0.0.1 ... linking ... done. Loading package old-time-1.0.0.1 ... linking ... done. Loading package random-1.0.0.1 ... linking ... done. (Friday,4041414 40692) *Main random (mkStdGen 123) :: (DayOfWeek, StdGen) (Tuesday,4961736 40692) *Main == import System.Random data DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Show, Read, Eq, Enum, Ord, Bounded) instance Random DayOfWeek where randomR (a,b) g = case (randomIvalInteger (toInteger (dow2Int a), toInteger (dow2Int b)) g) of (x, g) - (int2Dow x, g) where dow2Int Monday = 0 dow2Int Tuesday = 1 dow2Int Wednesday = 2 dow2Int Thursday = 3 dow2Int Friday = 4 dow2Int Saturday = 5 dow2Int Sunday = 6 int2Dow 0 = Monday int2Dow 1 = Tuesday int2Dow 2 = Wednesday int2Dow 3 = Thursday int2Dow 4 = Friday int2Dow 5 = Saturday int2Dow 6 = Sunday random g = randomR (minBound,maxBound) g randomIvalInteger :: (RandomGen g, Num a) = (Integer, Integer) - g - (a, g) randomIvalInteger (l,h) rng | l h = randomIvalInteger (h,l) rng | otherwise = case (f n 1 rng) of (v, rng') - (fromInteger (l + v `mod` k), rng') where k = h - l + 1 b = 2147483561 n = iLogBase b k f 0 acc g = (acc, g) f n acc g = let (x,g') = next g in f (n-1) (fromIntegral x + acc * b) g' iLogBase :: Integer - Integer - Integer iLogBase b i = if i b then 1 else 1 + iLogBase b (i `div` b) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
On Fri, May 01, 2009 at 01:08:26PM -0500, Thomas Hartman wrote: For quickchecking, the code below cheats by not defining the coarbitrary funciton, which I confess I don't really understand and never use. FWIW, QuickCheck 2 separates 'coarbitrary' in a different class. -- Felipe. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Generating random enums
On Fri, May 1, 2009 at 11:08 AM, Thomas Hartman tphya...@gmail.com wrote: For quickchecking, the code below cheats by not defining the coarbitrary funciton, which I confess I don't really understand and never use. coarbitrary is simple; it's used for generating arbitrary functions. If you don't know how to define it for your type, coarbitrary _ = id is a reasonable definition. But it's usually easy to define, and I'll show you how! But first, the motivation for its existence. You have an instance of arbitrary X, and an instance of arbitrary Y, and some transformations: transformX :: X - X transformY :: Y - Y prop_natural_transform :: (X - Y) - X - Bool prop_natural_transform f x = f (transformX x) == transformY (f x) This says, for all f, (f . transformX) = (transformY . f). A real example of a property similar to this is for map and reverse: prop_map_reverse :: Eq b = (a - b) - [a] - Bool prop_map_reverse f xs = map f (reverse xs) == reverse (map f xs) Now, how can QuickCheck generate functions to pass to these properties? The function f is *pure*; it can't use a random generator to determine what Y to output. What QuickCheck *can* do, however, is split the random generator at the point where it needs to create f, then uses coarbitrary to adjust the state of the generator based on the argument passed in: mkArbFunction :: forall a b. (Arbitrary a, Arbitrary b) = Gen (a - b) mkArbFunction = sized $ \size - do randomSource - rand let f :: a - b f x = generate size randomSource (coarbitrary x arbitrary) return f Inside of f, we have a single generator that is fixed; without coarbitrary, we would only be able to generate a single object of type b, the one that is a result of running arbitrary with that fixed generator. But with coarbitrary, the argument can affect the response! The simplest thing to do is to extract some random values from the generator before using it to generate the result: -- only works for non-negative values twist :: Int - Gen a - Gen a twist 0 g = g twist n g = do () - elements [(), ()] -- just make the random generator do some work coarbitrary (n-1) g It's really easy to implement coarbitrary for many types in terms of twist: data Color = Red | Green | Blue instance Arbitrary Color where arbitrary = elements [Red, Green, Blue] coarbitrary Red = twist 0 coarbitrary Green = twist 1 coarbitrary Blue = twist 2 instance Arbitrary a = Arbitrary (Maybe a) where arbitrary = oneOf [return Nothing, liftM Just arbitrary] coarbitrary Nothing = twist 0 coarbitrary (Just x) = twist 1 . coarbitrary x A better version of twist is in Test.QuickCheck with the name variant. In fact, for Bounded/Enum types like your code uses, it's easy to define coarbitrary from variant: coarbEnum :: (Bounded a, Enum a) = a - Gen b - Gen b coarbEnum a = variant (fromEnum a - fromEnum (minBound `asTypeOf` a)) -- ryan ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe