snoyberg / classy-prelude

A typeclass-based Prelude.
108 stars 15 forks source link

un-generalize ++ #85

Closed gregwebs closed 9 years ago

gregwebs commented 9 years ago

When ++ was generalized to Monoid it made sense because it was right before <> was introduced. Now that we have <>, generalizing ++ means

The only problem now is breaking existing code, which makes this change difficult

snoyberg commented 9 years ago

Actually, there's more going on here than that. Semigroup's append operator is <>, which conflicts with Data.Monoid. In my code, I use <> for things which are only Semigroup instances, ++ for things which are only Monoid, and for things which are both, it's a crap shoot. However, switching ++ to be list-specific would mean that we either lose the ability to use Semigroup or lose the Monoid version. I don't like either option.

gregwebs commented 9 years ago

I use <> for anything Semigroup or Monoid and ++ for anything List. I tend not to distinguish much between Semigroup and Monoid because my main usage of Semigroup is just a non-empty Monoid, so the usage of <> for either ends up being semantically identical.

We could debate about which approach is better, and I don't care much. The problem is that the current operation is different from the current Haskell Prelude for reasons that no longer matter. What I am proposing (except that you pointed out that <> is actually for Semigroup and not for Monoid in classy-prelude) is closer to what exists in the Haskell Prelude.

But the breakage may not be worth it.

snoyberg commented 9 years ago

There are actually some datatypes out there that provide Monoid instances but no Semigroup instances. That's when I'm force to use ++. This proposal would mean I'd have to use mappend instead.

gregwebs commented 9 years ago

Can you not write instance Semigroup MonoidDataType first? I am not afraid of using orphans in application code.

snoyberg commented 9 years ago

I am, I've had plenty of breakage in application code from orphans. They're not as bad as in library code, but it's still a problem. For example, I've had cases where GHC will sometimes successfully compile a module and sometimes not depending on the order of compilation of the modules. If it first compiles the module with the orphan instance, then that instance is in scope, otherwise it isn't.

gregwebs commented 9 years ago

I guess I should say that with the strategy we use we have no fear. We have separate Instance.hs modules so that the no-orphans warning is off in a limited scope, and those modules have basic imports and are at the root of the import hierarchy because they are essentially Type modules. This ends up giving the same effect as having a foo-instances package.

It might sound like extra work, but once you start the pattern it is easy. I think it is a lot more work to fear orphans.

snoyberg commented 9 years ago

To each his own. I had to ban orphans almost completely because some people on my team got lazy and started defining orphans for types defined in our own codebase for no good reason at all.

Anyway, I'm a pretty strong -1 on this change. Added to that the breakage as you mentioned, are you OK closing this?

gregwebs commented 9 years ago

There are 2 existing standards: 1) Prelude <> = Monoid append, ++ = List append 2) Semigroup <> = Semigroup append, ++ = List append

I don't think orphan management is a good reason for classy-prelude to create a new standard.

However, at this point there will probably be too much breakage, so I will have to fix it in antemodulum.