haskellmonomorphism-restriction

How to work around issue with ambiguity when monomorphic restriction turned *on*?


So, learning Haskell, I came across the dreaded monomorphic restriction, soon enough, with the following (in ghci):

Prelude> let f = print.show
Prelude> f 5

<interactive>:3:3:
    No instance for (Num ()) arising from the literal `5'
    Possible fix: add an instance declaration for (Num ())
    In the first argument of `f', namely `5'
    In the expression: f 5
    In an equation for `it': it = f 5

So there's a bunch of material about this, e.g. here, and it is not so hard to workaround. I can either add an explicit type signature for f, or I can turn off the monomorphic restriction (with ":set -XNoMonomorphismRestriction" directly in ghci, or in a .ghci file).

There's some discussion about the monomorphic restriction, but it seems like the general advice is that it is ok to turn this off (and I was told that this is actually off by default in newer versions of ghci).

So I turned this off.

But then I came across another issue:

Prelude> :set -XNoMonomorphismRestriction
Prelude> let (a,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int 

<interactive>:4:5:
    No instance for (System.Random.Random t0)
      arising from the ambiguity check for `g'
    The type variable `t0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance System.Random.Random Bool -- Defined in `System.Random'
      instance System.Random.Random Foreign.C.Types.CChar
        -- Defined in `System.Random'
      instance System.Random.Random Foreign.C.Types.CDouble
        -- Defined in `System.Random'
      ...plus 33 others
    When checking that `g' has the inferred type `System.Random.StdGen'
    Probable cause: the inferred type is ambiguous
    In the expression:
      let (a, g) = System.Random.random (System.Random.mkStdGen 4)
      in a :: Int
    In an equation for `it':
        it
          = let (a, g) = System.Random.random (System.Random.mkStdGen 4)
            in a :: Int

This is actually simplified from example code in the 'Real World Haskell' book, which wasn't working for me, and which you can find on this page: http://book.realworldhaskell.org/read/monads.html (it's the Monads chapter, and the getRandom example function, search for 'getRandom' on that page).

If I leave the monomorphic restriction on (or turn it on) then the code works. It also works (with the monomorphic restriction on) if I change it to:

Prelude> let (a,_) = System.Random.random (System.Random.mkStdGen 4) in a :: Int 
-106546976

or if I specify the type of 'a' earlier:

Prelude> let (a::Int,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
-106546976

but, for this second workaround, I have to turn on the 'scoped type variables' extension (with ":set -XScopedTypeVariables").

The problem is that in this case (problems when monomorphic restriction on) neither of the workarounds seem generally applicable.

For example, maybe I want to write a function that does something like this and works with arbitrary (or multiple) types, and of course in this case I most probably do want to hold on to the new generator state (in 'g').

The question is then: How do I work around this kind of issue, in general, and without specifying the exact type directly?

And, it would also be great (as a Haskell novice) to get more of an idea about exactly what is going on here, and why these issues occur..


Solution

  • When you define

    (a,g) = random (mkStdGen 4)
    

    then even if g itself is always of type StdGen, the value of g depends on the type of a, because different types can differ in how much they use the random number generator.

    Moreover, when you (hypothetically) use g later, as long as a was polymorphic originally, there is no way to decide which type of a you want to use for calculating g.

    So, taken alone, as a polymorphic definition, the above has to be disallowed because g actually is extremely ambiguous and this ambiguity cannot be fixed at the use site.

    This is a general kind of problem with let/where bindings that bind several variables in a pattern, and is probably the reason why the ordinary monomorphism restriction treats them even stricter than single variable equations: With a pattern, you cannot even disable the MR by giving a polymorphic type signature.

    When you use _ instead, presumably GHC doesn't worry about this ambiguity as long as it doesn't affect the calculation of a. Possibly it could have detected that g is unused in the former version, and treated it similarly, but apparently it doesn't.

    As for workarounds without giving unnecessary explicit types, you might instead try replacing let/where by one of the binding methods in Haskell which are always monomorphic. The following all work:

    case random (mkStdGen 4) of
        (a,g) -> a :: Int
    
    (\(a,g) -> a :: Int) (random (mkStdGen 4))
    
    do (a,g) <- return $ random (mkStdGen 4)
       return (a :: Int)    -- The result here gets wrapped in the Monad