Discussion:
Proposal: Add Applicative (and Monoid) instances in GHC.Generics
Li-yao Xia
2018-02-20 23:51:26 UTC
Permalink
I propose to implement Applicative (and Monoid) instances in
GHC.Generics, where possible.

Note that we already have Applicative instances for most of those types,
with the notable exceptions of sums (:+:) (for good reason), and K1
(which is basically Const).

This would make it possible to easily derive Applicative and Monoid for
generic product types. That can be useful to merge large configuration
records, for example.

For that purpose, the missing Applicative instance for K1 is the actual
limiting factor, as it is sufficient in order to derive the product
Monoid and Applicative via Generic and Generic1, respectively. The
Monoid instances for the GHC.Generics constructors might just be fluff
on top of this.

Current alternatives include: roll your own, semigroups [1], or some
generic programming library (e.g., one-liner). This proposal offers a
more lightweight solution.

To summarize, and to organize bikeshedding, I would split this proposal
in three parts, in order of perceived usefulness:

1. Add an instance (Monoid c => Applicative (K1 i c))

2. Add helpers

gmempty :: (Generic a, Applicative (Rep a)) => a
gmempty = to (pure ())

gmappend :: (Generic a, Applicative (Rep a)) => a -> a -> a
gmappend a b = to (from a <*> from b)

-- also gpure, gap for generic Applicative

3. Add Monoid instances

Regards,
Li-yao


[1]
https://hackage.haskell.org/package/semigroups-0.18.4/docs/Data-Semigroup-Generic.html
Ryan Scott
2018-02-21 18:16:54 UTC
Permalink
Post by Li-yao Xia
1. Add an instance (Monoid c => Applicative (K1 i c))
This seems like a no-brainer to me. +1
Post by Li-yao Xia
2. Add helpers
gmempty :: (Generic a, Applicative (Rep a)) => a
gmempty = to (pure ())
gmappend :: (Generic a, Applicative (Rep a)) => a -> a -> a
gmappend a b = to (from a <*> from b)
-- also gpure, gap for generic Applicative
I'm weakly -1 on this. I don't think GHC.Generics should be a place
where we collect combinators for generically implementing various type
class methods. I'd prefer that these be left to downstream libraries,
especially since they're usually extremely straightforward to
implement and wouldn't cross the Fairbairn threshold for me.
Post by Li-yao Xia
3. Add Monoid instances
I think I support this idea. But just to be sure we're on the same
page: can you say which instances in particular you're adding, and how
you'd implement them?

Ryan S.
Oleg Grenrus
2018-02-21 18:27:40 UTC
Permalink
For helpers see `semigroups` package, Data.Semigroup.Generic module [1]

I indeed don't see strong argument for moving them into `base`, only if
we want to provide default `Generic` implementations for `Semigroup` &
`Monoid`. I'm not sure we do?

Oleg

[1]
http://hackage.haskell.org/package/semigroups-0.18.4/docs/Data-Semigroup-Generic.html
Post by Ryan Scott
Post by Li-yao Xia
1. Add an instance (Monoid c => Applicative (K1 i c))
This seems like a no-brainer to me. +1
Post by Li-yao Xia
2. Add helpers
gmempty :: (Generic a, Applicative (Rep a)) => a
gmempty = to (pure ())
gmappend :: (Generic a, Applicative (Rep a)) => a -> a -> a
gmappend a b = to (from a <*> from b)
-- also gpure, gap for generic Applicative
I'm weakly -1 on this. I don't think GHC.Generics should be a place
where we collect combinators for generically implementing various type
class methods. I'd prefer that these be left to downstream libraries,
especially since they're usually extremely straightforward to
implement and wouldn't cross the Fairbairn threshold for me.
Post by Li-yao Xia
3. Add Monoid instances
I think I support this idea. But just to be sure we're on the same
page: can you say which instances in particular you're adding, and how
you'd implement them?
Ryan S.
_______________________________________________
Libraries mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Edward Kmett
2018-02-21 19:56:20 UTC
Permalink
+1 for adding the instances.

I'm fairly neutral on the helpers.

-Edward
Post by Ryan Scott
Post by Li-yao Xia
1. Add an instance (Monoid c => Applicative (K1 i c))
This seems like a no-brainer to me. +1
Post by Li-yao Xia
2. Add helpers
gmempty :: (Generic a, Applicative (Rep a)) => a
gmempty = to (pure ())
gmappend :: (Generic a, Applicative (Rep a)) => a -> a -> a
gmappend a b = to (from a <*> from b)
-- also gpure, gap for generic Applicative
I'm weakly -1 on this. I don't think GHC.Generics should be a place
where we collect combinators for generically implementing various type
class methods. I'd prefer that these be left to downstream libraries,
especially since they're usually extremely straightforward to
implement and wouldn't cross the Fairbairn threshold for me.
Post by Li-yao Xia
3. Add Monoid instances
I think I support this idea. But just to be sure we're on the same
page: can you say which instances in particular you're adding, and how
you'd implement them?
Ryan S.
_______________________________________________
Libraries mailing list
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Li-yao Xia
2018-02-22 21:52:41 UTC
Permalink
Thanks everyone. So there is agreement on having Applicative and Monoid
instances, and on not having additional helpers for generic deriving.
That's fine to me.
Post by Ryan Scott
Post by Li-yao Xia
3. Add Monoid instances
I think I support this idea. But just to be sure we're on the same
page: can you say which instances in particular you're adding, and how
you'd implement them?
The main Monoid instances would be:

Monoid (f p) => Monoid (M1 i c f p) -- newtype-derived
Monoid a => Monoid (K1 i a p) -- newtype-derived
(Monoid (f p), Monoid (g p)) => Monoid ((f :*: g) p) -- product monoid
Monoid (U1 p) -- unit-like

No instance for (:+:).

Now that you mention it, I'm not sure about adding Monoid instances for
the type constructors involved in Generic1, they would be (all
newtype-derived):

Monoid p => Monoid (Par1 p)
Monoid (f p) => Monoid (Rec1 f p)
Monoid (f (g p)) => Monoid ((f :.: g) p)

Is ther an opinion against that?

I forgot to mention Semigroup instances. They would follow the same
structure, and for completeness we can also have

Semigroup (V1 p)

This enables a more general "generic semigroup" than via Applicative.

Li-yao
Ryan Scott
2018-02-23 13:46:47 UTC
Permalink
Those Monoid instances (and Semigroup instance for V1) you propose
sound reasonable.

I'd also be fine with adding Monoid instances for Par1, Rec1, and
(:.:), since we already define other instances like Eq, Ord, Read, and
Show for them. Besides, if we don't add them this time around, someone
else will just ask for them later :)

Ryan S.

Loading...