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

RULES exploration #34

Open
treeowl opened this issue Jul 29, 2021 · 12 comments
Open

RULES exploration #34

treeowl opened this issue Jul 29, 2021 · 12 comments
Labels
enhancement New feature or request performance

Comments

@treeowl
Copy link
Collaborator

treeowl commented Jul 29, 2021

Opening this up to explore some things we might want to smash with RULES.

  1. toView (lift m) = m >>= single. I believe this should help lift m <|> n and m <|> lift n.
  2. As discussed, toView (fromView m) = m. Among other things, this should improve pure a <|> m and m <|> pure a.
@treeowl treeowl added performance enhancement New feature or request labels Jul 30, 2021
@treeowl
Copy link
Collaborator Author

treeowl commented Jul 31, 2021

If lift is inlined, then that first rule I suggested wouldn't be necessary, as the toView . fromView rule will take care of it. However, we might not want to do that, because there are other games we can play with lift when we can see it.

lift m >>= f ==> fromView $ m >>= toView . f

looks promising.

In combination with that, we might want to reassociate binds:

(m >>= n) >>= f ==> m >>= \x -> n x >>= f

The big question is whether we can convince either of these rules to fire. There may also be ways to change the SeqT representation to do this stuff without rules.

@dagit
Copy link
Owner

dagit commented Jul 31, 2021

My understanding of how RULES would work for this (since almost everything here is a type class method) is that we'll need to provide standalone functions that implement those methods and write the RULES against those.

Phase control might help with some of the ordering of applying the rules. Phases start at some high number and go until phase 0 runs. So you could have a toView . fromView rule run at a lower phase number than the lift m >>= f rule.

I really like these kinds of optimizations but having multiple rules does come at a cost. As you know, when you have multiple rules you have to make sure they are confluent. And they have to respect strictness. So it's not just the normal term equality confluence but I believe it's a stronger version that respects strictness?

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 2, 2021

As part of the toView . fromView ==> id and fromView . toView ==> id rules, we probably want to write INLINE wrapper functions that apply toView and fromView and dispatch to (possibly INLINABLE) worker functions. The reason is that we want to be really sure the simplifier can see those collapsible pairs, even when we don't necessarily want to inline the things around them.

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 2, 2021

Confluence is a desirable property, but not something we can generally guarantee within the confines of GHC's system. In particular, rules firing can affect inlining decisions and such that may affect whether other rules will fire. Even rules that appear confluent, therefore, may not actually be. I'm guessing you're actually concerned about the much weaker guarantee that rules should not change program meaning. That is very important, yes. I don't think that should really be particularly hard, including with regard to strictness, under the assumption that the underlying Monad instances are strictly lawful. That means we should document that RULES might change the behavior of code written for weird things like Control.Monad.Trans.State.Lazy and Control.Monad.Trans.Tardis. I don't imagine that there's any good reason to apply a logic monad transformer to such beasts anyway, but I could be wrong. Without that assumption, we can't write any useful rules whatsoever.

@dagit
Copy link
Owner

dagit commented Aug 2, 2021

Confluence is the property mentioned in the papers and documentation. I forget exactly where I read it, but I know I saw confluence and preserving strictness both mentioned.

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 2, 2021

Confluence is discussed on the Haskell Wiki. It's mentioned in the GHC documentation, but only in passing. The basic idea is that you should try to avoid writing rules in such a way that the choice of which rule to fire next (when there are multiple choices) will have a dramatic impact on performance.

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 2, 2021

And yes, we should be careful to preserve strictness. But as I said above, we can do very little without assuming strictly lawful Monad instances. It's up to you whether to make that assumption. I would suggest doing so, and documenting it in some detail.

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 2, 2021

Awkwardly, SeqT itself is not a strictly lawful monad. Note that undefined >>= pure is not undefined, but rather something closer to lift undefined. Can we fix that by, e.g., making >>= strict in its first argument?

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 2, 2021

Or do we want to just say that users shouldn't seq on SeqT and should always toView it first?

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 2, 2021

I just realized that fromView . toView ==> id is invalid, since it loses laziness. But toView . fromView ==> id, which is much more useful, should be fine.

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 3, 2021

I realized we actually can use a slightly more limited rule even without assuming the user's Monad instance is lawful:

toView (fromView m) = m >>= \x -> return x

This is the result of just inlining and reducing. In principle, GHC could do this without a rule, but I don't know if it does.

@treeowl
Copy link
Collaborator Author

treeowl commented Aug 3, 2021

Indeed, GHC seems not to do this itself, so let's try.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request performance
Projects
None yet
Development

No branches or pull requests

2 participants