The trouble is that when you say
gen <- ... :: (forall s. ST s (Gen (PrimState (ST s))))
s
is now fixed to whatever s
runST
provides -- i.e. we cannot consider it a type variable as your signature would have you believe[1]. When the compiler says "rigid type variable", this is what it means. To reinforce that it is fixed, let's refer to it as S1
for this answer.
Notice that
let go :: Int -> ST s Int
is equivalent to
let go :: forall s. Int -> ST s Int
i.e., go
must work for any s
. But then you say
v <- uniformR (1,x) gen
which attempts to bind a computation of type ST S1 <something>
. go
is supposed to work with any s
, not just S1
, so this is an error. The correct signature for go
is Int -> ST S1 Int
, but of course we just made up S1
for argument's sake, and the true S1
has no name in the source file, so go
cannot be given a signature, even though it is well-typed.
[1] Oh, you have ScopedTypeVariables
on, so it looks like the forall
is there because you are trying to scope s
. That doesn't work -- scoped variables only apply to the body of the function with the forall
. You can solve this by moving the signature to the left of the <-
:
(gen :: Gen (PrimState (ST s))) <- initialize ...
after which s
will be properly scoped.