Breaking Changes and Long Term Support Haskell

Edward Kmett ekmett at gmail.com
Thu Oct 22 16:32:08 UTC 2015


On Thu, Oct 22, 2015 at 9:29 AM, Geoffrey Mainland <mainland at apeiron.net>
wrote:

> Thanks to you and Dan [1], I now have a greater understanding and
> appreciation for where the committee has been coming from. My new
> understanding is that the changes that were formalized in AMP, FTP, and
> MRP were the basis for the committee's creation. It also seems that
> there are more changes in the pipeline that have not yet been made into
> proposals, e.g., pulling (>>) out of Control.Monad [2]. Part of
> "stability" is signaling change as far ahead as possible. The committee
> has put a lot of effort into this, which I appreciate! However, as each
> of these proposal has come down the pipeline, I never realized that they
> were part of a larger master plan.
>

The "master plan" where (>>) is concerned is that it'd be nice to get
Traversable down to a minimal state and to eliminate unnecessary
distinctions in the Prelude between things like mapM and traverse. Right
now they have different type constraints, but this is entirely a historical
artifact. But it causes problems, we have a situation where folks have
commonly optimized (>>) but left (*>) unfixed. This yields different
performance for mapM_ and traverse_. A consequence of the AMP is that the
neither one of those could be defined in terms of the other (*>) has a
default definition in terms of (<*>). (>>) has a default definition in
terms of (>>=). With two places where optimizations can happen and two
different definitions for operations that are logically required to be the
same thing we can and do see rather radically different performance between
these two things.

This proposal is something that was put out as a sort of addendum to the
Monad of No Return proposal for discussion, but unlike MRP has no
particular impact on a sacred cow like return. We have yet to put together
a timeline that incorporates the (>>) changes from MRP.

1) What is the master plan, and where is it documented, even if this
> document is not up to the standard of a proposal? What is the final
> target, and when might we expect it to be reached? What is in the
> pipeline after MRP?
>
> Relatedly, guidance on how to write code now so that it will be
> compatible with future changes helps mitigate the stability issue.
>

The current plans more or less stop with finishing the MonadFail proposal,
getting Semigroup in as a superclass of Monoid, and incorporating some
additional members into Floating. The working document for the timeline
going forward is available here:

https://ghc.haskell.org/trac/ghc/wiki/Status/BaseLibrary

>
> 2) How can I write code that makes use of the Prelude so that it will
> work with every new GHC release over the next 3 years? 5 years? For
> example, how can I write a Monad instance now, knowing the changes that
> are coming, so that the instance will work with every new GHC release
> for the next 3 years? 5 years? If the answer is "you can't," then when
> might I be able to do such a thing? As of 8.4? 8.6? I'm embarrassed to
> say I don't know the answer!
>

We have a backwards facing "3 release policy" that says it should always be
possible to write code that works backwards for 3 releases. This means that
changes like moving fail out of Monad will take 5 years. However,
maintaining both that and a _forward facing_ 3 release policy would mean
that any change that introduced a superclass would take something like 9
years of intermediate states that make no sense to complete. *9 years to
move one method.*

Now looking forward. You can write code today with 7.10 that will work
without warnings until 8.2. That happens to be 3 releases. In 8.4 you'll
start to get warnings about Semigroup and MonadFail changes, but looking at
it as 3 releases going forward in 8.0 you can just write the instances and
your code would be warning free forward for 3 releases. In 8.6 those
changes go into effect, but you will have been able to make the code
changes that you need to accomodate 8.6 since 8.0.

The current roadmap happens to give you a 3 year sliding window.

Finally, if none of these changes broke Prelude backwards compatibility,
> far fewer people would be complaining :)


If none of our changes were ever able to break Prelude backwards
compatibility the same people who have been complaining about the utter
lack of progress for the previous 17 years and that nearly exploded the
community 2 years ago would be complaining, and based on polling and
discusssions that is actually a much larger group. The AMP passed nearly
unanimously.


> Of course, we can't always make
> progress without breaking things, but a more deliberative process might
> offer an opportunity to make progress while still preserving backwards
> compatibility. Take AMP for example. There were at least two [3] [4]
> proposals for preserving backwards compatibility. Investigating them
> would have taken time and delayed AMP, yes, but why the rush?
>

We've been talking about various superclass defaulting proposals for the
better part of a decade and no progress has been made. The rush was that
we'd been letting them block every previous discussion, and that the
concrete plan with an actual implementation that was on hand was a very
popular proposal even without that mitigation strategy.

3) Can we have a process that allows more deliberation over, and wider
> publicity for, changes that break backwards compatibility? The goal of
> such a process would not be to prevent change, but to allow more time to
> find possible solution to the issue of backwards compatibility.
>
> My proposal for a low-traffic mailing list where all proposals were
> announced was meant to provide wider publicity.
>

I don't think anybody has an objection to wider visibility of proposals
that affect things mentioned in the Haskell Report.


> Personally, I think these proposals do indeed fix a lot of warts in the
> language. As a researcher who uses actively uses Haskell every day,
> these warts have had approximately zero impact on me for the past
> (almost) decade, and I would be perfectly content if they were never
> fixed. The only pain I can recall enduring is having to occasionally
> write an orphan Applicative instance. I have been importing Prelude
> hiding mapM for years. I have been importing Control.Applicative for
> years. Neither has been painful.


And yet the vast preponderance of public opinion lies in the other camp.
The "change nothing" policy had an iron grip on the state of affairs for 17
years and there were serious cracks starting to form from the appearance
that nothing could ever be fixed if the Prelude was affected in any way.

The only thing that broke with that was when Ian Lynagh unilaterally
removed Eq and Show as superclasses of Num. That was more or less the first
glimmer that the world wouldn't end if deliberated changes were made to the
Prelude.

Dealing with AMP? I'm working on a
> collaborative research project that is stuck on 7.8 because of AMP. I
> agree, that seems silly, but whether or not it is silly, it is an impact
> I feel.
>

What changes did you face beyond writing

instance Functor Foo where
  fmap = liftM

instance Applicative Foo where
  pure = return
  (<*>) = ap

that is AMP related?

Maybe there are a lot of people who answer "yes" to both questions. I
> would like to know! But does having return in the Monad class really
> cause anyone anything other than existential pain?
>

The MRP is by far the most marginal proposal on the table. This is why it
remains *just a proposal* and not part of the roadmap. That said, moving
return to a top level definition will mean that more code that is compiled
will be able to infer an Applicative constraint.

The other proposals that are on the roadmap on the other hand defend a lot
better.

The (>>) fragment of MRP fixes rampant performance regressions, however. We
went to generalize the implementation of mapM_ to use (*>) internally and
found performance regressions within base itself due to instances that are
optimized inconsistently. This informed the design here. More code will
infer with weaker Applicative constraints, Traversable can eventually be
simplified, and folks like Simon Marlow who have folks internally at
Facebook use mapM will just have their code "work" in Haxl. I can answer
"yes" to both of your questions here.

The continued existence of fail in Monad on the other hand has caused a
great deal of pain in instances for things like `Either a` for years. To
supply `fail`, we used to incur a needless Error a constraint. We can be
more precise and remove a potential source of partiality from a lot of
code. I can answer "yes" to both of your questions here.

The lack of Semigroup as a superclass of Monoid has meant that the Monoid
instance for Maybe adds a unit to something that already has a unit. It
means that First and Last, etc. all useless tack an extra case that
everyone has to handle in. It has dozens of knock-on consequences. Much
code that currently only needs a semigroup falls back on a monoid because
of the lack of a proper class relationship or gets duplicated. I can answer
"yes" to both of your questions here.

The numerics changes to Floating mean that Haskell numerics just have awful
precision. Adding expm1, etc. to Floating means that people will be able to
write decent numerical code without having to choose between generality
(using exp from Floating that works everywhere) and accuracy. I can answer
"yes" to both of your questions here.

-Edward
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-prime/attachments/20151022/d65704d8/attachment.html>


More information about the Haskell-prime mailing list