2
votes

I was playing around with the random function that gives an infinite list of random values as shown in Chapter 9 of "Learn You a Haskell for Great Good". The code goes like this:

randoms' :: (RandomGen g, Random a) => g -> [a]
randoms' gen = let (value, newGen) = random gen
               in value : randoms' newGen

In order to record the random generators, I changed randoms' a bit to the following:

randoms'' :: (RandomGen g, Random a) => g -> [(a, g)]
randoms'' gen = let (value, newGen) = random gen
                in (value, newGen) : randoms'' newGen

It works as expected.

Then I rewrote it in the style of list comprehension:

randoms''' :: (RandomGen g, Random a) => g -> [(a, g)]
randoms''' gen = random gen : [random gen' | (_, gen') <- randoms''' gen]

This time, the complier gives the error: Could not deduce (Random a0) arising from a use of ‘randoms'''’...... The type variable ‘a0’ is ambiguous...

However, if I specify the type of randoms''' with concrete types, e.g.,

randoms''' :: StdGen -> [(Int, StdGen)]
randoms''' gen = random gen : [random gen' | (_, gen') <- randoms''' gen]

it works fine again and gives exactly the same result as randoms''.

I wonder why the type inference works for randoms'' but fails for randoms'''. Can anyone tell me why these two are not equivalent and how to fix the code of randoms'''?

Furthermore, I experimented with the test code having the similar structure:

generate :: (Integral a, RealFrac b) => a -> (b,a)
generate m = let x = 1.2^^m in (x, ceiling x)

foo :: (Integral g, RealFrac a) => g -> [(a,g)]
foo gen = let (value, newGen) = generate gen
          in (value, newGen) : foo newGen

foo' :: (Integral g, RealFrac a) => g -> [(a, g)]
foo' gen = generate gen : [generate gen' | (_, gen') <- foo' gen]

foo'' :: (Integral g, RealFrac a) => g -> [(a, g)]
foo'' gen = [generate gen' | gen' <- gen : map snd (foo'' gen)]

It turns out foo, foo' and foo'' all work fine. Apparently, it is not a problem of monomorphism vs polymorphism. It seems to be a problem specific to random.

1
You throw away the actual value generated by randoms''' ((_, gen') <- ...) so the compiler has no idea at which type to instantiate the use of randoms'''; there is no reason for it to pick the same type a that is the output, nor any other particular type, so it is ambiguous. In the monomorphic case, there is no instantiation possible - naturally it may only pick Int for the recursive case.user2407038

1 Answers

2
votes
(_, gen') <- randoms''' gen

Which type has _ here? You probably think it should be the a given in randoms''''s signature. However, it's arbitrary. It might be (), it might be Char. Everything could work here, since you don't use it anyway. We can force it to have the same type as random gen':

randoms''' gen = random gen : [random gen' `asTypeOf` x | x@(_, gen') <- randoms''' gen]

asTypeOf is a standard function and basically a type constrained version of const:

asTypeOf :: a -> a -> a
asTypeOf = const

With this function, we can determine the type of x and therefore the type of _, since the type of random gen' is known.