Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help/advice upgrading MonadRandom to 1.3.0? #179

Open
byorgey opened this issue Jan 15, 2025 · 1 comment
Open

Help/advice upgrading MonadRandom to 1.3.0? #179

byorgey opened this issue Jan 15, 2025 · 1 comment

Comments

@byorgey
Copy link
Member

byorgey commented Jan 15, 2025

Trying to build the MonadRandom package with random-1.3 fails with the following compilation error (see byorgey/MonadRandom#54):

Control/Monad/Trans/Random/Strict.hs:285:10: error: [GHC-18872]
    • Couldn't match type: MutableGen f0 (RandT g m)
                     with: RandGen g
        arising from a use of ‘System.Random.Internal.$dmuniformByteArrayM’
      The type variable ‘f0’ is ambiguous
    • In the expression:
        System.Random.Internal.$dmuniformByteArrayM
          @(RandGen g) @(RandT g m)
      In an equation for ‘uniformByteArrayM’:
          uniformByteArrayM
            = System.Random.Internal.$dmuniformByteArrayM
                @(RandGen g) @(RandT g m)
      In the instance declaration for
        ‘StatefulGen (RandGen g) (RandT g m)’
    • Relevant bindings include
        uniformByteArrayM :: Bool
                             -> Int -> RandGen g -> RandT g m Data.Array.Byte.ByteArray
          (bound at Control/Monad/Trans/Random/Strict.hs:285:10)
    |
285 | instance (Monad m, RandomGen g) => StatefulGen (RandGen g) (RandT g m) where

It seems this is a type error in some generic auto-generated code for uniformByteArrayM, which was added to the StatefulGen class in 1.3. Looking at the default type signature for uniformByteArrayM (here: https://hackage.haskell.org/package/random-1.3.0/docs/src/System.Random.Internal.html#uniformByteArrayM), we see this:

  default uniformByteArrayM ::
    (RandomGen f, FrozenGen f m, g ~ MutableGen f m) => Bool -> Int -> g -> m ByteArray
  uniformByteArrayM isPinned n g = modifyGen g (uniformByteArray isPinned n)

I think I see how this is supposed to work: MutableGen has a functional dependency from its output to its first parameter, so g ~ MutableGen f m should be enough to fix f. The problem is really that we don't have a FrozenGen g (RandT g m) instance. After carrying out a bunch of type substitutions, I am pretty sure the instance I want looks something like this:

instance FrozenGen g (RandT g m) where
  type MutableGen g (RandT g m) = RandGen g
  freezeGen = _
  overwriteGen = _

However, this fails because it overlaps with an instance in System.Random.Internal!

Control/Monad/Trans/Random/Strict.hs:293:8: error: [GHC-34447]
    Conflicting family instance declarations:
      MutableGen g (RandT g m) = RandGen g
        -- Defined at Control/Monad/Trans/Random/Strict.hs:293:8
      MutableGen (StateGen g) m = StateGenM g
        -- Defined in module System.Random.Internal
    |
293 |   type MutableGen g (RandT g m) = RandGen g

So now I am at a bit of a loss how to proceed. Any advice, pointing out something I am doing wrong, etc. would be very welcome!

@lehins
Copy link
Contributor

lehins commented Jan 23, 2025

RandT is essentially a pure generator in StateT, which is very similar to how StateGen in random works. This means that neither StateGen nor RandT can have an instance for ThawedGen, but they can have an instance for FrozenGen type class. Having the FrozenGen type class instance implemented will give you the definition of uniformByteArrayM for free, but you can always overwrite it with a custom definition.

In your particular case there are two ways that it can be solved I believe:

  • Either not adding FrozenGen instance and adding a custom definition for uniformByteArrayM, which I could help you with.
  • or adding FrozenGen instance to use default implementation, but it can't be overlapping, which means you need an extra type definition, eg:
newtype PureRandGen g = PureRandGen g

instance FrozenGen (PureRandGen g) (RandT g m) where
  type MutableGen (PureRandGen g) (RandT g m) = RandGen g
  freezeGen = _
  overwriteGen = _

This is because we decided early on in the random interface that it is the generator that decides the instance, not the monad it is suppose to work in. That is why addition of RandGen was need in the first place with random-1.2

If you look into random generators, they all have a mutable and immutable counterpart, that is the part that is missing. Unfortunately, just the g in RandGen g is not enough for ghc to be able infer instances unambiguously.

Let me know if it is not clear and I could try to find some time and submit a PR into MonadRandom that would it compatible with random-1.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants