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

Addition of state token to MonadRandom #79

Merged
merged 2 commits into from
Apr 9, 2020

Conversation

lehins
Copy link
Collaborator

@lehins lehins commented Apr 8, 2020

@Shimuuar I think I found a simple and elegant solution to our Frozen conundrum. Very similar to what you did in #71 except using fun deps.

Instance form MWC becomes really nice:

instance (s ~ PrimState m, PrimMonad m) => Random.MonadRandom Gen s m where
  newtype Frozen Gen = Frozen { unFrozen :: Seed }
  thawGen (Frozen seed) = restore seed

This approach adds very clutter around. It makes slightly uglier for PureGen and other instances that don't care about a state token (eg. IORef), but that's the price we'll have to pay for generality.

Let me know what you think.

@lehins
Copy link
Collaborator Author

lehins commented Apr 8, 2020

In fact FunctionalDependencies are not only not required, but can also cause problems, so I'll remove usage of the extension. Scratch that, fundeps are required after all.

@curiousleo
Copy link
Collaborator

curiousleo commented Apr 8, 2020

Instance form MWC becomes really nice:

instance (s ~ PrimState m, PrimMonad m) => Random.MonadRandom Gen s m where
  newtype Frozen Gen = Frozen { unFrozen :: Seed }
  thawGen (Frozen seed) = restore seed

For comparison, without to this PR, MWC can be made an instance of MonadRandom as follows:

instance (s ~ PrimState m, PrimMonad m) => MonadRandom (MWC.Gen s) m where
    newtype Frozen (MWC.Gen s) = FrozenGen MWC.Seed
    thawGen (FrozenGen s) = MWC.restore s

(From the module docs.)

The benefit of this change is not clear to me.

Would users need to enable -XUndecidableInstances in order to use random if this PR is merged?

@curiousleo
Copy link
Collaborator

curiousleo commented Apr 8, 2020

Conflict resolved here: https://github.com/idontgetoutmuch/random/compare/interface-to-performance...idontgetoutmuch:monad-random-state-token-rebased?expand=1

Feel free to force-push b83bebd to monad-random-state-token to resolve the merge conflict.

Edit: done.

@curiousleo curiousleo mentioned this pull request Apr 8, 2020
@curiousleo curiousleo force-pushed the monad-random-state-token branch from e26eb04 to b83bebd Compare April 8, 2020 12:11
@lehins
Copy link
Collaborator Author

lehins commented Apr 8, 2020

No, they won't have to. Only when you create an instance you might need to use that extension.

Would users need to enable -XUndecidableInstances in order to use random if this PR is merged?

Benefit is that Frozen no longer depends on a state token, which is not a real problem, but conceptually it is wrong. Which @Shimuuar pointed out and with I do agree.

The benefit of this change is not clear to me.

Here is an example type signature restricted to MWC.Gen that shows the problem:

thawGen :: Frozen (Gen s) -> ST s (Gen s)

Which with this PR becomes:

thawGen :: Frozen Gen -> ST s (Gen s)

Because Frozen is a seed it does not belong inside of ST.

@lehins
Copy link
Collaborator Author

lehins commented Apr 8, 2020

Oh, by the way, totally forgot to mention that this futzing around in attempt to solve this issue resulted in a discover of a bug in ghc: https://gitlab.haskell.org/ghc/ghc/issues/18021

@Shimuuar
Copy link

Shimuuar commented Apr 8, 2020

And to think that I dropped three parameter MonadRandom as obviously ugly!

class Monad m => MonadRandom (g :: * -> *) s m | m -> s where
  • kind annotation for g could be dropped. It's inferrable.
  • I'm a bit worried about m -> s dependency. Could it lead to problems when same monad is used with different generators. I tried to run small experiment but it did w ork out.

@lehins
Copy link
Collaborator Author

lehins commented Apr 8, 2020

@Shimuuar Here is a PR that creates a few instances for this approach, so funcdeps seem to work out fine, in fact without it is a bit problematic for instances that rely on MonadIO

I don't see how that can happen:

Could it lead to problems when same monad is used with different generators.

It is only a problem if generator and monad are both the same in two different instances

Good catch, I experimenting with poly kinded alternatives, I'll remove the annotation.

kind annotation for g could be dropped. It's inferrable.

Ugliness is certainly a matter of perspective :)

And to think that I dropped three parameter MonadRandom as obviously ugly!

What do you think? Which one of these type signatures is more appealing:

uniformInt :: MonadRandom g s m => g s -> m Int

or this

uniformInt :: MonadRandom g m => g (StateToken g m) -> m Int

So, overall, @Shimuuar do you think is a viable solution to the problem from #71 and overall a decent design?

@Shimuuar
Copy link

Shimuuar commented Apr 8, 2020

I don't see how that can happen:

To elaborate more I meant that m -> s instead of g m -> s could possibly lead to problems. In MonadRandom ((PureGen StdGen) s (StateT StdGen IO)) s should be StdGen while for MonadRandom ((MutGen StdGen) s (StateT StdGen IO)) it becomes RealWorld. So for same m we get different s! It's something that wouldn't work with type families. I'll try to investiagate how this is supposed to work.

So, overall, @Shimuuar do you think is a viable solution to the problem from #71 and overall a decent design?

Strongly, yes!

@lehins lehins merged commit a45f298 into interface-to-performance Apr 9, 2020
curiousleo added a commit that referenced this pull request Apr 9, 2020
curiousleo pushed a commit that referenced this pull request May 19, 2020
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

Successfully merging this pull request may close these issues.

3 participants