Skip to content

Monoid instances#113

Closed
coot wants to merge 1 commit intohaskell:mainfrom
coot:coot/monoid-instances
Closed

Monoid instances#113
coot wants to merge 1 commit intohaskell:mainfrom
coot:coot/monoid-instances

Conversation

@coot
Copy link
Copy Markdown

@coot coot commented Apr 3, 2026

If a is a Monoid and m a monad then m a is a Monoid in a natural way.
base provides Monoid a => Monoid (IO a) instance.

This instance is handy when one needs to do a foldMap over some monadic
computation.

@coot coot force-pushed the coot/monoid-instances branch from 60a5c91 to df16f8f Compare April 3, 2026 19:50
@L0neGamer
Copy link
Copy Markdown
Collaborator

Great work, likely mergeable soon; since this should resolve #88, could you add similar instances for Backwards and Lift?

@coot coot force-pushed the coot/monoid-instances branch from df16f8f to ffdeb17 Compare April 4, 2026 09:08
@coot
Copy link
Copy Markdown
Author

coot commented Apr 4, 2026

I also added AccumT instance, and relaxed constraints for some instances (from Monad to Applicative).

@coot coot force-pushed the coot/monoid-instances branch from ffdeb17 to fcfddd8 Compare April 4, 2026 09:30
Copy link
Copy Markdown
Collaborator

@L0neGamer L0neGamer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see that the mappend implementations are neccessary pre base 4.11, good catch!

I'll approve and merge this today, assuming I get time to manually test it a bit.

@meooow25
Copy link
Copy Markdown

meooow25 commented Apr 4, 2026

Just passing by, note that these instances have been discussed previously and were not added at the time.
https://hub.darcs.net/ross/transformers/issue/38

@sjshuck
Copy link
Copy Markdown
Collaborator

sjshuck commented Apr 4, 2026

https://hub.darcs.net/ross/transformers/issue/38

GitHub issue #10 for reference

@coot
Copy link
Copy Markdown
Author

coot commented Apr 4, 2026

From the original issue:

David Menendez notes that "there are at least three reasonable instances of Semigroup > and Monoid for ReaderT."

instance (Applicative m, Monoid a) => Monoid (ReaderT r m a) where
    mempty = pure mempty
    mappend a b = mappend <$> a <*> b

instance (Alternative m) => Monoid (ReaderT r m a) where
    mempty = empty
    mappend = (<|>)

instance (Monoid (m a)) => Monoid (ReaderT r m a) where
    mempty = ReaderT $ mempty
    mappend a b = ReaderT $ mappend a b

"In the absence of a principled reason to prefer one over the others and a general consensus, I think it’s better not to choose."

Therefore, I'm closing this request.

The second one is an an implementation of Alternative (ReaderT r m), and thus it can be ruled out.

The only difference between 1 and 3 would be if the monoid is not implemented as liftA2 (<>). I actually know examples of such monads (I haven't pushed them to Hackage yet):

For them even with 1 one can get the right semantics if the {First,Last}ToFinish is the top transformer. Still, using 3 rather than 1 seems more appropriate to me.

@sjshuck
Copy link
Copy Markdown
Collaborator

sjshuck commented Apr 4, 2026

Regarding strictness, 1 (which is this PR) will cause problems if m is strict, which is the common case (e.g. failure effect or IO). It will build up thunks with <> the way foldl and Control.Monad.Trans.Writer.Strict do. I recognize that IO is already like that, but IMO we shouldn't encourage it. If someone wants that behavior, let them use Ap.

@sjshuck
Copy link
Copy Markdown
Collaborator

sjshuck commented Apr 4, 2026

2 is Alt.


3 is interesting. It says, wherever there appears a Monoid in our stack, let that propagate. With ReaderT that propagation has simple enough semantics, although it does propagate the aforementioned IO instance in the extremely common case of ReaderT r IO a, which I opined on already. StateT and other transformers is where doing that gets dicey. You need Monoid (m (s, a)), which, if m is a reader with the proposed instance or if m is IO, incurs a Monoid constraint on s. IOW a <> b will run both state computations and monoidally combine their final states. In particular, after a finishes and its mappend-result is lingering as a thunk, the state is reset back to the pre-a value when entering b. I would consider that surprising behavior in, say, StateT s IO—the real world is obviously not reset like that.

@L0neGamer
Copy link
Copy Markdown
Collaborator

@sjshuck and I had a chat and we think that the behaviour suggested in this PR can be suitably regained by using Ap instead of having these instances in transformers. As #10 says, there are other instances that could apply here, and it seems unlikely to us that this Ap like implementation is more or less suitable than the others. Alt and Ap can be used for their interpretation of <>, and option 3 is odd enough that we don't want to support it. As sjshuck says, strictness may be an issue in some cases.

As a result (apologies for being long winded, and for asking for so many adjustments), we don't think we will merge this change. I'll close #88 soon as well with a similar message.

Thank you for your contributions all.

@L0neGamer L0neGamer closed this Apr 4, 2026
@github-project-automation github-project-automation bot moved this from In Progress to Done in Ouroboros Network Apr 4, 2026
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.

4 participants