[ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative

Richard Eisenberg rae at richarde.dev
Fri Apr 10 13:51:32 UTC 2020


Despite being the one to articulate "fully settled" and how to make builders with it, I now favor the module-based approach. Thanks, Joachim, for writing out pros and cons. I agree with the pros and con you listed with the module-based approach. Let's also look at some pros of the record-based approach:

> ➕ A single entity one can import, reexport, even rebind
> ➕ A single entity that can carry the documentation
> ➕ One module can export multiple builders

With either of two module-system-improvement proposals (https://github.com/ghc-proposals/ghc-proposals/pull/283 <https://github.com/ghc-proposals/ghc-proposals/pull/283> or https://github.com/ghc-proposals/ghc-proposals/pull/295 <https://github.com/ghc-proposals/ghc-proposals/pull/295>), these pros carry over to the module-based approach.

The reason I switched camps, from favoring record-based to favoring module-based, is this argument, by John Ericson: https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-606109628 <https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-606109628>  Here, John is reacting to my complaint that the module-based solution will, to be ergonomic, require either #283 or #295. But John points out that we don't need to wait for #283 or #295: we can implement the module-based system right away. It will be usable (but sometimes clunky) right away. It will satisfy the problems in the motivation right away. All #283/#295 would do is make it less clunky.

Put another way: let's imagine a Haskell with either of #283 or #295 implemented. Then, I think, the module-based approach would be a very clear win, over a record-based approach with its one-off "fully settled" and strange restrictions on types. (The builder must have *some* record type, but any record type would do. There's nothing else like that anywhere.) So by choosing the record-based approach, we're cutting off access to the right design in the limit. Of course, #283/#295 are large proposals, still technically under discussion (but very quiet), and either one has a sizable implementation burden. I would never want to accept this current proposal in a way that would depend on #283/#295. But, as argued above, the module-based approach does not depend on these large proposals.

My thoughts on the finer points:

- I think the selectors should be in scope. It's simpler and less ambiguous this way, given the fact that one module can alias several modules to, say, M. I don't feel strongly on this point, though, and I see the arguments in the other direction.
- I think this extension would be considerably more ergonomic if the new operators were not named (>>=), etc., but instead something like qualifiedBind, qualifiedThen, etc. This means that defining modules needn't be delicate around importing the Prelude, and importing modules don't need to worry about record selectors causing name clashes, etc., if they are not using this new extension.

As we decide on this proposal, I would also like us to think about how this scales to other applications of rebindable syntax. For example, maybe we want M.if or M.[ x, y, z ] someday. I think the design *does* scale in such a way, which perhaps paves the way to deprecating -XRebindableSyntax.

Richard

> On Apr 9, 2020, at 6:16 PM, Joachim Breitner <mail at joachim-breitner.de> wrote:
> 
> Dear Committe,
> 
> Proposal: 
> https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do.rst
> Discussion (long, sorry):
> https://github.com/ghc-proposals/ghc-proposals/pull/216
> 
> 
> Summary:
> 
> Over a year ago (on my birthday then) Arnaud created  a “local do”
> proposal that would be a more targetted variant of RebindableSyntax,
> just for “do”. In June, we sent it back because a simple syntactic
> desugaring to records didn’t quite seem right (bad type inference).
> 
> In March, the authors can back with an alternative, which was using a
> Module name instead of a value of record type to indicate that monadic
> operations to use. This nicely solved the type system issues and meant
> that the translation can happen (in principle) in the parser or renamer
> stage. But some of us noticed that a builder record is nicer after all,
> and we can fix the type system issues, mostly by introducing a new concept
> of “fully settled type”; with analogies to TH stage restrictions.
> 
> The authors updated the proposal accordingly, but also list the alternatives
> in the documents.
> 
> Based on the GitHub thread we have varying opinions among the committee.
> Nevertheless, I think the authors have done a great and patient job so far, so
> we owe them a hopefully conclusive discussion.
> 
> The main question we have to decide is:
> 
>    record-based   or    module based
> 
> 
> Record based:
> ➕ A single entity one can import, reexport, even rebind
> ➕ A single entity that can carry the documentation
> ➕ One module can export multiple builders
> ➕ Looking forward, the builder could be dynamically constructed
>   (i.e. a local value)
> ➕ Concept of fully settled may be useful elsewhere in the future
>   and can be expanded
> ➖ Needs a new concept of “fully settled” that we don’t have elsewhere
> ➖ Initially, “fully settled” introduces staging restrictions;
>   builder values may not be usable everywhere where they are in scope.
> ➖ Lots of fluff on the defining side
>   (define a likely one-off record + a value)
> ➖ May require extensions (e.g. RankNTypes, ImpredicativeTypes)
>   on the defining side, even for a builder for the “normal” Monad
>   (see https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-600746723
>   for an example for the previous two points)
> ➕ Some compositionality (functions modifying builders), but
> ➖ not as universal as one would hope, as there is not a single builder type
>   (different qualified monads likely use different record type)
> ➕ Can support “passing arguments to do” via `(monadBuilder @Maybe).do`
>   or `(b cfg).do`
>   (once the notion of “fully settled” is powerful enough)
> 
> Module based:
> ➕ Simpler to specify and understand:
>   Only affects parsing, possibly renaming. No interactions with the type system.
> ➕ Works out of the box with, say, `Prelude.` as the qualifier
> ➕ Benefits from future improvements to the module system
> ➖ Would need separate syntax for “passing arguments to do”, should we want that
> ➕ But if we had that, it can implement the record-based approach, by passing a
>   recoord to a suitable qualified do monad, as Iavor observes:
>   https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-598859245
> 
> The module-based approach would additionally raises the question whether
> * the desugaring to M.(>>) means (>>) as provided by (some) M”,
>   akin to how plain do notation works.
> * the desugaring to M.(>>) means just that (and requires (>>) to be imported as well),
>   akin to how RebindableSyntax works
> 
> 
> There was also a brief discussion of whether this should extend the set
> of operations involved to a `last` function that is used in the
> translation rule for a single-statement do notation, but it did not
> catch on.
> 
> 
> Recommendation:
> 
> While both approaches are reasonable and have their merits, I recommend
> to accept the Module based approach. It supports most use-cases
> presented so far, in particular the Linear.do as envisioned by the
> authors, so it seems good enough™.
> Furthermore, it certainly is significantly simpler, given that it can
> be specified purely in terms of naming things, so we have a higher
> chance that this will work well with other existing and future language
> features.
> 
> Should the committee follow that decision, I recommend to pick the
> variant where the value does not need to be in scope, so that its
> mechanism is close to the normal do notation, and that you can write
> 
> import Linear (runLinear, other, stuff)
>>   Linear.do { … }
> 
> without mucking with qualified imports or shadowing (>>). It seems odd
> to require the user to add ((>>), (>>=), fail) to an import list when
> you don’t actually mention that name anywhere.
> 
> 
> 
> 
> Cheers,
> Joachim
> 
> 
> -- 
> Joachim Breitner
>  mail at joachim-breitner.de
>  http://www.joachim-breitner.de/
> 
> 
> _______________________________________________
> ghc-steering-committee mailing list
> ghc-steering-committee at haskell.org
> https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-steering-committee/attachments/20200410/3499f690/attachment.html>


More information about the ghc-steering-committee mailing list