#216: Qualified Do again, recommendation: accept the alternative

Dear Committe, Proposal: https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... 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-6007467... 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-5988592... 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@joachim-breitner.de http://www.joachim-breitner.de/

As one of the authors, I'm going to engage only lightly in this conversation. Just a few comments, for the moment ➖ 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-6007467... for an example for the previous two points)
I don't believe this to hold. Impredicative record fields are plain Haskell 2010. 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.
I am, to be honest, very uncomfortable with the idea: if I explicitly
didn't import something, I don't want it to be used in my program. A
counter-argument is that types appear in desugared programs which were not
imported, so maybe I'm overreacting. But I'm still uncomfortable. And it
doesn't look like it's less of a complication than the fully-settled
business.
Overall, I personally quite prefer the record-based alternative.
On Thu, Apr 9, 2020 at 7:17 PM Joachim Breitner
Dear Committe,
Proposal:
https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... 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-6007467... 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-5988592...
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@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hi, Am Freitag, den 10.04.2020, 09:58 +0200 schrieb Spiwack, Arnaud:
I am, to be honest, very uncomfortable with the idea: if I explicitly didn't import something, I don't want it to be used in my program. A counter-argument is that types appear in desugared programs which were not imported, so maybe I'm overreacting. But I'm still uncomfortable.
I am surprised this is so controversial. There are many things in Haskell that are used that are not imported: * The desugaring of plain do notation (!) * The desugaring of if-then-else (no need to have True/False in scope) * The desugaring of boolean guards (again, no need to have True/False in scope) * Instances * And because of that, `foo.bar` according to RecordDotSyntax will not require `bar` to be in scope, as this is just an instance accessed via HasField "bar" (if I am not mistaken) In contrast, there is nothing where you have to import some `foo` when you don't actually mention `foo` in your source code. Which seems a pretty reasonable rule: Import the things you write; no more, no less! (Ok, there is one thing that was added recently: With Coercible you need to import the data constructor to coerce under a type constructors. Which I am not fond of.) And note that you’d still be explicitly importing the module name, which is the only thing you’d write in your program. So you can’t claim you “didn’t import something”. And: The same question actually applies to the record variant as well!, If we’d go for the record variant, would you expect the user to write import Linear (builder) or import Linear (builder, BuilderRecord(..)) in order to use `Linear.builder.do {…}`?
And it doesn't look like it's less of a complication than the fully- settled business.
To me, it does. Significantly so. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

On Fri, Apr 10, 2020 at 10:43 AM Joachim Breitner
I am surprised this is so controversial. There are many things in Haskell that are used that are not imported:
* The desugaring of plain do notation (!) * The desugaring of if-then-else (no need to have True/False in scope) * The desugaring of boolean guards (again, no need to have True/False in scope) * Instances * And because of that, `foo.bar` according to RecordDotSyntax will not require `bar` to be in scope, as this is just an instance accessed via HasField "bar" (if I am not mistaken)
In contrast, there is nothing where you have to import some `foo` when you don't actually mention `foo` in your source code.
Which seems a pretty reasonable rule: Import the things you write; no more, no less!
I wanted to add, for the record, that Joachim's argument convinced me that, indeed, were we to go for the module-qualified do approach, we probably shouldn't require the names to be in scope. However, it makes the module-qualified approach more counter-intuitive to me: it doesn't make sense to me to use the namespace `M` to refer to a term which is not in this namespace. Obviously, Joachim, you have a different intuition about this.

About whether names must be in scope (only): What do module prefixes in code mean? I claim: they refer to a set of in-scope identifiers. That's it. Because of the way Haskell allows module aliasing, they do not refer to, say, a compilation unit, or some `module` structure. A module prefix identifies just a flat set of identifiers. This "set" view works nicely both with module aliasing and the way that Haskell specifies what "module M" means in an export list. Since a module prefix refers to a set of in-scope identifiers, it seems to make sense only to have the same meaning with M.do syntax. With the "identifiers do not need to be in scope" approach, then the M in M.do is now referring, I think, to a set of `module` structures that have been aliased to M in the import list. We then have to look in the export lists of each of those modules to see what is available. And what if multiple modules in the same set have bind operators in their export lists? That would be ambiguous, I suppose. Now, what if multiple modules in the module set export disjoint subsets of the operators? (For example, we have `import M1 as M` and `import M2 as M`, where `M1` exports `(>>)` and `M2` exports `(>>=)`.) I suppose we'd combine them. My problem is that we would have to specify all of these rules with the "out of scope" interpretation. With the in-scope interpretation, all of these answers follow directly from the specification. Much simpler! All that said, it seems the majority favor the out-of-scope interpretation. I truly don't feel strongly and am happy to go with that. I just wanted to expand my argument slightly to see if it won any of you over. Richard
On Apr 14, 2020, at 7:58 AM, Spiwack, Arnaud
wrote: On Fri, Apr 10, 2020 at 10:43 AM Joachim Breitner
mailto:mail@joachim-breitner.de> wrote: I am surprised this is so controversial. There are many things in Haskell that are used that are not imported: * The desugaring of plain do notation (!) * The desugaring of if-then-else (no need to have True/False in scope) * The desugaring of boolean guards (again, no need to have True/False in scope) * Instances * And because of that, `foo.bar` according to RecordDotSyntax will not require `bar` to be in scope, as this is just an instance accessed via HasField "bar" (if I am not mistaken)
In contrast, there is nothing where you have to import some `foo` when you don't actually mention `foo` in your source code.
Which seems a pretty reasonable rule: Import the things you write; no more, no less!
I wanted to add, for the record, that Joachim's argument convinced me that, indeed, were we to go for the module-qualified do approach, we probably shouldn't require the names to be in scope.
However, it makes the module-qualified approach more counter-intuitive to me: it doesn't make sense to me to use the namespace `M` to refer to a term which is not in this namespace. Obviously, Joachim, you have a different intuition about this. _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

In GHC we have two concepts:
* Qualified names like M.foo, which always mean "the foo imported from module M - albeit with import-qualified you can change the local name"
* Original names, also typically written M.foo, meaning "the foo defined in module M"
In list comprehensions, conditionals etc, we always desugar to original names; it doesn't matter if they are in scope. You don't even have to import the Prelude. But GHC knows the defining module.
With this "does not have to be in scope" business, you are inventing (and requiring someone to implement) a new scheme:
* Quasi-original names: M.foo means "the foo exported by module M"
I'm not at all keen this new mechanism
* Presumably there is no opportunity to do the import-qualified thing? Or maybe you can say "import This.That as M ()", thus importing nothing but giving a local name M?
* Presumably you must any other M.foo that is already in scope?
But I can see why you want this, because you don't want to list all those operations one by one. Exactly! You want to group them into a ... record! You want import Linear( linear ), and then you can say linear.do { ... }.
I'm pretty strongly against inventing quasi-original names. The module system is surprising complex already. I suppose you can always define an auxiliary module to do the grouping, thus
import Linear.Monad as L -- Exports the operators for linear-do
Simon
From: ghc-steering-committee

Hi, Am Dienstag, den 14.04.2020, 11:36 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
Qualified names like M.foo, which always mean “the foo imported from module M – albeit with import-qualified you can change the local name”
but it’s not that simple: What if I write import qualified M1 as M import qualified M2 as M and then use M.foo? Well, it resolves the foo imported from M1, if M1 exports it. Or from M2, if M2 exports it. But what if both export it? Then we get an “ambiguity error”… Unless M1.foo and M2.foo, via re-exports, point to the same original name. What is proposed for M.(>>) here is the same logic, with the addition that it doesn’t matter whether you used “hiding ((>>))” somewhere in these imports, or imported (), to keep the nice existing rule “You only import what you explicitly mention” In terms of implementation I don’t think the burden is high; GHC already has that logic for the “M.foo is not in scope, did you want to add foo to the export list in line x” error message. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Hello,
as the Github discussion is pretty long, I thought it might be useful to
have a summary of the observations to the issue being discussed, namely how
to resolve the names in `M.do`.
Without the special syntax suggested by Joachim, a programmer has the
following options to make `M.do` work, and I've marked some (potential)
pros/cons:
1. import MyMonad as M
(+) other operations do not need to be qualified
(-) unqualified `>>=` may be ambiguous
2. import qualified MyMonad as M
(-) other operations need to be qualified
(+) unqualified `>>=` is not ambiguous
3. import qualified MyMonas as M
import MyMonad (other,operations)
(+) other operations do not need to be qualified
(+) unqualifed `>>=` is not ambiguous
(-) requires two imports
Joachim's proposal aims to improve on (3) by allowing programmers to write:
4. import MyMonad as M (other,operations)
(+) other operations do not need to be qualified
(+) unqualified `>>=` are not ambiguous
The idea is that the renamer would compute the original name to use in the
desugaring by seeing if `>>=` is exported by one of the modules imported
with alias `M`. GHC certainly has the required information, and I doubt
this would be hard to implement.
As I said in my previous e-mail, I don't have a strong feeling about the
choice we make, but I wanted to make sure that we are all discussing the
same thing.
-Iavor
On Tue, Apr 14, 2020 at 8:28 AM Joachim Breitner
Hi,
Am Dienstag, den 14.04.2020, 11:36 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
Qualified names like M.foo, which always mean “the foo imported from module M – albeit with import-qualified you can change the local name”
but it’s not that simple: What if I write
import qualified M1 as M import qualified M2 as M
and then use M.foo? Well, it resolves the foo imported from M1, if M1 exports it. Or from M2, if M2 exports it. But what if both export it? Then we get an “ambiguity error”… Unless M1.foo and M2.foo, via re-exports, point to the same original name.
What is proposed for M.(>>) here is the same logic, with the addition that it doesn’t matter whether you used “hiding ((>>))” somewhere in these imports, or imported (), to keep the nice existing rule “You only import what you explicitly mention”
In terms of implementation I don’t think the burden is high; GHC already has that logic for the “M.foo is not in scope, did you want to add foo to the export list in line x” error message.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

I’m sorry I’m slow, but I just don’t yet understand what is being proposed.
Joachim launched the discussion herehttps://mail.haskell.org/pipermail/ghc-steering-committee/2020-April/001533..... I believe that he is advocating section 6.1 in the proposal. Moreover he has some variant of it in mind. I asked some questionshttps://mail.haskell.org/pipermail/ghc-steering-committee/2020-April/001545.... about the variant that may or may not have been answered.
It would be really helpful to have a single source of truth. Perhaps someone (Joachim or anyone else) can write a Google doc that describes, as precisely as possible, the proposal that he is advocating. Or get the proposal authors to do so. Just saying “a variant where the value does not need to be in scope” does not count as a specification (to me).
Iavor’s writeup below is helpful, but I fall over immediately. First, I believe that his (1-4) things are not *design* alternatives; they are all things the programmer can write, all under Section 6.1 of the proposal (unmodified). Is that right.
Then.
1. import MyMonad as M
(+) other operations do not need to be qualified
(-) unqualified `>>=` may be ambiguous
What are “other operations”?
Why might “>>=” be ambiguous? I think the answer is: because MyMonad must export it (to use in M.do), but it may already be in scope from the Prelude.
I’m strongly inclined against inventing new complexity in the module system, unless it is absolutely unavoidable. More to specify, more to explain, more to understand, more to implement.
Simon
From: ghc-steering-committee
Qualified names like M.foo, which always mean “the foo imported from module M – albeit with import-qualified you can change the local name”
but it’s not that simple: What if I write import qualified M1 as M import qualified M2 as M and then use M.foo? Well, it resolves the foo imported from M1, if M1 exports it. Or from M2, if M2 exports it. But what if both export it? Then we get an “ambiguity error”… Unless M1.foo and M2.foo, via re-exports, point to the same original name. What is proposed for M.(>>) here is the same logic, with the addition that it doesn’t matter whether you used “hiding ((>>))” somewhere in these imports, or imported (), to keep the nice existing rule “You only import what you explicitly mention” In terms of implementation I don’t think the burden is high; GHC already has that logic for the “M.foo is not in scope, did you want to add foo to the export list in line x” error message. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.demailto:mail@joachim-breitner.de http://www.joachim-breitner.de/https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joachim-breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7C66be21fcd8d74e052a3608d7e08f1857%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637224777509071750&sdata=TWMDeXho9obNmy%2FW0DJQn5Lq5PmQcWXh4NTRoLt2bvo%3D&reserved=0 _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.orgmailto:ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committeehttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering-committee&data=02%7C01%7Csimonpj%40microsoft.com%7C66be21fcd8d74e052a3608d7e08f1857%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637224777509081745&sdata=ytwWAGYW8iNbV2MNYf7hiUWvwOHat1noPOu0swTYbgw%3D&reserved=0

Hi, Am Donnerstag, den 16.04.2020, 14:48 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
It would be really helpful to have a single source of truth. Perhaps someone (Joachim or anyone else) can write a Google doc that describes, as precisely as possible, the proposal that he is advocating. Or get the proposal authors to do so. Just saying “a variant where the value does not need to be in scope” does not count as a specification (to me).
the variant is described in 6.1.2 of the proposal: https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... has a spec (“resolves to any (>>=) that is exported by any module aliased by the name M, independently of whether it is in scope (i.e. imported).” and an I think enlightening example (albeit with an odd duplication of imports).
Then. 1. import MyMonad as M (+) other operations do not need to be qualified (-) unqualified `>>=` may be ambiguous
What are “other operations”?
Anything else exported from MyMonad that the user might want to use.
Why might “>>=” be ambiguous? I think the answer is: because MyMonad must export it (to use in M.do), but it may already be in scope from the Prelude.
Correct.
I’m strongly inclined against inventing new complexity in the module system, unless it is absolutely unavoidable. More to specify, more to explain, more to understand, more to implement.
And you are sure that the complexity of this is more than that of “fully settled” types? Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

| And you are sure that the complexity of this is more than that of
| “fully settled” types?
I didn't say that. But I am sure that it's more complicated than 6.1 without 6.1.2! If I understand aright, the *only* gain is that one can omit three words:
import qualified MyMonad
Simon
| -----Original Message-----
| From: ghc-steering-committee

Hi, Am Donnerstag, den 16.04.2020, 15:01 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
And you are sure that the complexity of this is more than that of “fully settled” types?
I didn't say that. But I am sure that it's more complicated than 6.1 without 6.1.2! If I understand aright, the *only* gain is that one can omit three words:
import qualified MyMonad
that’s fair… I guess with 6.1 we would imply To use qualified do, the recommended idiom is to import the module (1) qualified and (2) without an explicit import list. Put like this, I think I can live it with it. I still find it odd that those people who like to list all imports explicitly would be forced to write import qualified MyMonad ((>>)) foo = MyMonad.do { a; b } and then even have to adjust the import list when they write foo = MyMonad.do { _ <- a; b } instead. But yeah, saying “just don’t list your imports when you qualify your module import” works indeed. With that, I am happy to support 6.1 as well (but still prefer 6.1.2) Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

| the variant is described in 6.1.2 of the proposal:
PS: Sorry for missing that. (It has not been mentioned in any of the discussion.)
Simon
| -----Original Message-----
| From: ghc-steering-committee

If we allow "M.do" without explicitly importing M, building the module dependency graph suddenly requires parsing the entire module rather than just the preamble. How much of a concern is this for compilation times? Apart from that possible concern, I support both of Joachim's recommendations. On Tue, Apr 14, 2020, at 03:47, Richard Eisenberg wrote:
About whether names must be in scope (only):
What do module prefixes in code mean? I claim: they refer to a set of in-scope identifiers. That's it. Because of the way Haskell allows module aliasing, they do not refer to, say, a compilation unit, or some `module` structure. A module prefix identifies just a flat set of identifiers. This "set" view works nicely both with module aliasing and the way that Haskell specifies what "module M" means in an export list.
Since a module prefix refers to a set of in-scope identifiers, it seems to make sense only to have the same meaning with M.do syntax. With the "identifiers do not need to be in scope" approach, then the M in M.do is now referring, I think, to a set of `module` structures that have been aliased to M in the import list. We then have to look in the export lists of each of those modules to see what is available. And what if multiple modules in the same set have bind operators in their export lists? That would be ambiguous, I suppose. Now, what if multiple modules in the module set export disjoint subsets of the operators? (For example, we have `import M1 as M` and `import M2 as M`, where `M1` exports `(>>)` and `M2` exports `(>>=)`.) I suppose we'd combine them. My problem is that we would have to specify all of these rules with the "out of scope" interpretation. With the in-scope interpretation, all of these answers follow directly from the specification. Much simpler!
All that said, it seems the majority favor the out-of-scope interpretation. I truly don't feel strongly and am happy to go with that. I just wanted to expand my argument slightly to see if it won any of you over.
Richard
On Apr 14, 2020, at 7:58 AM, Spiwack, Arnaud
wrote: On Fri, Apr 10, 2020 at 10:43 AM Joachim Breitner
wrote: I am surprised this is so controversial. There are many things in Haskell that are used that are not imported:
* The desugaring of plain do notation (!) * The desugaring of if-then-else (no need to have True/False in scope) * The desugaring of boolean guards (again, no need to have True/False in scope) * Instances * And because of that, `foo.bar` according to RecordDotSyntax will not require `bar` to be in scope, as this is just an instance accessed via HasField "bar" (if I am not mistaken)
In contrast, there is nothing where you have to import some `foo` when you don't actually mention `foo` in your source code.
Which seems a pretty reasonable rule: Import the things you write; no more, no less!
I wanted to add, for the record, that Joachim's argument convinced me that, indeed, were we to go for the module-qualified do approach, we probably shouldn't require the names to be in scope.
However, it makes the module-qualified approach more counter-intuitive to me: it doesn't make sense to me to use the namespace `M` to refer to a term which is not in this namespace. Obviously, Joachim, you have a different intuition about this. _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

HI, Am Dienstag, den 14.04.2020, 10:00 -0500 schrieb Eric Seidel:
If we allow "M.do" without explicitly importing M, building the module dependency graph suddenly requires parsing the entire module rather than just the preamble. How much of a concern is this for compilation times?
that isn’t the plan. You still have to import a module called M! The question is whether you _also_ have to import M.(>>), or whether it’s import to import a _module_ and give it a name. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

I see, I misunderstood the point of contention. I still support your recommendations, but it seems like a rather minor issue. My guess is that (in particular if we keep the standard names for the operations) most people will opt to use qualified imports, which means the identifiers will all be in scope anyway. On Tue, Apr 14, 2020, at 10:19, Joachim Breitner wrote:
HI,
Am Dienstag, den 14.04.2020, 10:00 -0500 schrieb Eric Seidel:
If we allow "M.do" without explicitly importing M, building the module dependency graph suddenly requires parsing the entire module rather than just the preamble. How much of a concern is this for compilation times?
that isn’t the plan. You still have to import a module called M! The question is whether you _also_ have to import M.(>>), or whether it’s import to import a _module_ and give it a name.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

On Tue, 14 Apr 2020 at 16:00, Eric Seidel
If we allow "M.do" without explicitly importing M, building the module dependency graph suddenly requires parsing the entire module rather than just the preamble. How much of a concern is this for compilation times?
That would be a big deal for GHC, which currently relies on being able to parse just the header to determine the module dependency graph. Cheers Simon
Apart from that possible concern, I support both of Joachim's recommendations.
About whether names must be in scope (only):
What do module prefixes in code mean? I claim: they refer to a set of in-scope identifiers. That's it. Because of the way Haskell allows module aliasing, they do not refer to, say, a compilation unit, or some `module` structure. A module prefix identifies just a flat set of identifiers. This "set" view works nicely both with module aliasing and the way that Haskell specifies what "module M" means in an export list.
Since a module prefix refers to a set of in-scope identifiers, it seems to make sense only to have the same meaning with M.do syntax. With the "identifiers do not need to be in scope" approach, then the M in M.do is now referring, I think, to a set of `module` structures that have been aliased to M in the import list. We then have to look in the export lists of each of those modules to see what is available. And what if multiple modules in the same set have bind operators in their export lists? That would be ambiguous, I suppose. Now, what if multiple modules in the module set export disjoint subsets of the operators? (For example, we have `import M1 as M` and `import M2 as M`, where `M1` exports `(>>)` and `M2` exports `(>>=)`.) I suppose we'd combine them. My problem is that we would have to specify all of these rules with the "out of scope" interpretation. With the in-scope interpretation, all of these answers follow directly from the specification. Much simpler!
All that said, it seems the majority favor the out-of-scope interpretation. I truly don't feel strongly and am happy to go with that. I just wanted to expand my argument slightly to see if it won any of you over.
Richard
On Apr 14, 2020, at 7:58 AM, Spiwack, Arnaud
wrote: On Fri, Apr 10, 2020 at 10:43 AM Joachim Breitner < mail@joachim-breitner.de> wrote:
I am surprised this is so controversial. There are many things in Haskell that are used that are not imported:
* The desugaring of plain do notation (!) * The desugaring of if-then-else (no need to have True/False in scope) * The desugaring of boolean guards (again, no need to have True/False in scope) * Instances * And because of that, `foo.bar` according to RecordDotSyntax will not require `bar` to be in scope, as this is just an instance accessed via HasField "bar" (if I am not mistaken)
In contrast, there is nothing where you have to import some `foo` when you don't actually mention `foo` in your source code.
Which seems a pretty reasonable rule: Import the things you write; no more, no less!
I wanted to add, for the record, that Joachim's argument convinced me
On Tue, Apr 14, 2020, at 03:47, Richard Eisenberg wrote: that, indeed, were we to go for the module-qualified do approach, we probably shouldn't require the names to be in scope.
However, it makes the module-qualified approach more counter-intuitive
to me: it doesn't make sense to me to use the namespace `M` to refer to a term which is not in this namespace. Obviously, Joachim, you have a different intuition about this.
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hi, Am Donnerstag, den 16.04.2020, 13:12 +0100 schrieb Simon Marlow:
On Tue, 14 Apr 2020 at 16:00, Eric Seidel
wrote: If we allow "M.do" without explicitly importing M, building the module dependency graph suddenly requires parsing the entire module rather than just the preamble. How much of a concern is this for compilation times?
That would be a big deal for GHC, which currently relies on being able to parse just the header to determine the module dependency graph.
But nobody is suggesting this, right? Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

That’s what I thought, but thankfully Joachim has clarified that the module has to be explicitly imported, the only question is whether the identifiers have to be brought in scope as part of the import. Sent from my iPhone
On Apr 16, 2020, at 07:12, Simon Marlow
wrote:
On Tue, 14 Apr 2020 at 16:00, Eric Seidel
wrote: If we allow "M.do" without explicitly importing M, building the module dependency graph suddenly requires parsing the entire module rather than just the preamble. How much of a concern is this for compilation times? That would be a big deal for GHC, which currently relies on being able to parse just the header to determine the module dependency graph.
Cheers Simon
Apart from that possible concern, I support both of Joachim's recommendations.
On Tue, Apr 14, 2020, at 03:47, Richard Eisenberg wrote:
About whether names must be in scope (only):
What do module prefixes in code mean? I claim: they refer to a set of in-scope identifiers. That's it. Because of the way Haskell allows module aliasing, they do not refer to, say, a compilation unit, or some `module` structure. A module prefix identifies just a flat set of identifiers. This "set" view works nicely both with module aliasing and the way that Haskell specifies what "module M" means in an export list.
Since a module prefix refers to a set of in-scope identifiers, it seems to make sense only to have the same meaning with M.do syntax. With the "identifiers do not need to be in scope" approach, then the M in M.do is now referring, I think, to a set of `module` structures that have been aliased to M in the import list. We then have to look in the export lists of each of those modules to see what is available. And what if multiple modules in the same set have bind operators in their export lists? That would be ambiguous, I suppose. Now, what if multiple modules in the module set export disjoint subsets of the operators? (For example, we have `import M1 as M` and `import M2 as M`, where `M1` exports `(>>)` and `M2` exports `(>>=)`.) I suppose we'd combine them. My problem is that we would have to specify all of these rules with the "out of scope" interpretation. With the in-scope interpretation, all of these answers follow directly from the specification. Much simpler!
All that said, it seems the majority favor the out-of-scope interpretation. I truly don't feel strongly and am happy to go with that. I just wanted to expand my argument slightly to see if it won any of you over.
Richard
On Apr 14, 2020, at 7:58 AM, Spiwack, Arnaud
wrote: On Fri, Apr 10, 2020 at 10:43 AM Joachim Breitner
wrote: I am surprised this is so controversial. There are many things in Haskell that are used that are not imported:
* The desugaring of plain do notation (!) * The desugaring of if-then-else (no need to have True/False in scope) * The desugaring of boolean guards (again, no need to have True/False in scope) * Instances * And because of that, `foo.bar` according to RecordDotSyntax will not require `bar` to be in scope, as this is just an instance accessed via HasField "bar" (if I am not mistaken)
In contrast, there is nothing where you have to import some `foo` when you don't actually mention `foo` in your source code.
Which seems a pretty reasonable rule: Import the things you write; no more, no less!
I wanted to add, for the record, that Joachim's argument convinced me that, indeed, were we to go for the module-qualified do approach, we probably shouldn't require the names to be in scope.
However, it makes the module-qualified approach more counter-intuitive to me: it doesn't make sense to me to use the namespace `M` to refer to a term which is not in this namespace. Obviously, Joachim, you have a different intuition about this. _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

On Thu, 16 Apr 2020 at 13:27, Eric Seidel
That’s what I thought, but thankfully Joachim has clarified that the module has to be explicitly imported, the only question is whether the identifiers have to be brought in scope as part of the import.
Sorry I should have read the rest of the thread before responding. Thanks! Simon
Sent from my iPhone
On Apr 16, 2020, at 07:12, Simon Marlow
wrote: On Tue, 14 Apr 2020 at 16:00, Eric Seidel
wrote: If we allow "M.do" without explicitly importing M, building the module dependency graph suddenly requires parsing the entire module rather than just the preamble. How much of a concern is this for compilation times?
That would be a big deal for GHC, which currently relies on being able to parse just the header to determine the module dependency graph.
Cheers Simon
Apart from that possible concern, I support both of Joachim's recommendations.
About whether names must be in scope (only):
What do module prefixes in code mean? I claim: they refer to a set of in-scope identifiers. That's it. Because of the way Haskell allows module aliasing, they do not refer to, say, a compilation unit, or some `module` structure. A module prefix identifies just a flat set of identifiers. This "set" view works nicely both with module aliasing and the way that Haskell specifies what "module M" means in an export list.
Since a module prefix refers to a set of in-scope identifiers, it seems to make sense only to have the same meaning with M.do syntax. With the "identifiers do not need to be in scope" approach, then the M in M.do is now referring, I think, to a set of `module` structures that have been aliased to M in the import list. We then have to look in the export lists of each of those modules to see what is available. And what if multiple modules in the same set have bind operators in their export lists? That would be ambiguous, I suppose. Now, what if multiple modules in the module set export disjoint subsets of the operators? (For example, we have `import M1 as M` and `import M2 as M`, where `M1` exports `(>>)` and `M2` exports `(>>=)`.) I suppose we'd combine them. My problem is that we would have to specify all of these rules with the "out of scope" interpretation. With the in-scope interpretation, all of these answers follow directly from the specification. Much simpler!
All that said, it seems the majority favor the out-of-scope interpretation. I truly don't feel strongly and am happy to go with that. I just wanted to expand my argument slightly to see if it won any of you over.
Richard
On Apr 14, 2020, at 7:58 AM, Spiwack, Arnaud
wrote: On Fri, Apr 10, 2020 at 10:43 AM Joachim Breitner < mail@joachim-breitner.de> wrote:
I am surprised this is so controversial. There are many things in Haskell that are used that are not imported:
* The desugaring of plain do notation (!) * The desugaring of if-then-else (no need to have True/False in scope) * The desugaring of boolean guards (again, no need to have True/False in scope) * Instances * And because of that, `foo.bar` according to RecordDotSyntax will not require `bar` to be in scope, as this is just an instance accessed via HasField "bar" (if I am not mistaken)
In contrast, there is nothing where you have to import some `foo` when you don't actually mention `foo` in your source code.
Which seems a pretty reasonable rule: Import the things you write; no more, no less!
I wanted to add, for the record, that Joachim's argument convinced me
On Tue, Apr 14, 2020, at 03:47, Richard Eisenberg wrote: that, indeed, were we to go for the module-qualified do approach, we probably shouldn't require the names to be in scope.
However, it makes the module-qualified approach more
counter-intuitive to me: it doesn't make sense to me to use the namespace `M` to refer to a term which is not in this namespace. Obviously, Joachim, you have a different intuition about this.
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

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-6061096... https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-6061096... 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
wrote: Dear Committe,
Proposal: https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... 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-6007467... 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-5988592...
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@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hello,
My thoughts on this are on the git-hub thread, as I've been involved in the
discussion for quite a while.
I am in alignment with Joachim, that we should accept the module-based
variant of the proposal.
I don't have strong feelings on if the implicit names used by the
transformation should be in scope or just accessible via an import.
However, I think he makes a convincing argument that it would be pretty
weird to require names to be in scope that are not visible anywhere in the
source code, so following the choice we already made for ordinary `do` does
make sense to me.
-Iavor
PS: Arnaud, not that it matters very much, but RankNTypes are not part of
Haskell 2010.
On Fri, Apr 10, 2020 at 6:51 AM Richard Eisenberg
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 or 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-6061096... 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
wrote: Dear Committe,
Proposal:
https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... 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-6007467... 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-5988592...
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@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

HI, I prefer the module-based approach too. I agree that names such as (>>=) should not be explicitly imported if they are not used in the code explicitly. I also support the following Richard's idea:
- 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.
This makes it easier to define such modules and use them if the new
extension is not enabled.
Vitaly
пт, 10 апр. 2020 г. в 18:28, Iavor Diatchki
Hello,
My thoughts on this are on the git-hub thread, as I've been involved in the discussion for quite a while.
I am in alignment with Joachim, that we should accept the module-based variant of the proposal.
I don't have strong feelings on if the implicit names used by the transformation should be in scope or just accessible via an import. However, I think he makes a convincing argument that it would be pretty weird to require names to be in scope that are not visible anywhere in the source code, so following the choice we already made for ordinary `do` does make sense to me.
-Iavor PS: Arnaud, not that it matters very much, but RankNTypes are not part of Haskell 2010.
On Fri, Apr 10, 2020 at 6:51 AM Richard Eisenberg
wrote: 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 or 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-6061096... 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
wrote: Dear Committe,
Proposal:
https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... 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-6007467... 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-5988592...
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@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hi,
I also lean towards the module-based approach.
Regards,
Alejandro
El dom., 12 abr. 2020 a las 8:02, Vitaly Bragilevsky (
HI,
I prefer the module-based approach too. I agree that names such as (>>=) should not be explicitly imported if they are not used in the code explicitly.
I also support the following Richard's idea:
- 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.
This makes it easier to define such modules and use them if the new extension is not enabled.
Vitaly
пт, 10 апр. 2020 г. в 18:28, Iavor Diatchki
: Hello,
My thoughts on this are on the git-hub thread, as I've been involved in the discussion for quite a while.
I am in alignment with Joachim, that we should accept the module-based variant of the proposal.
I don't have strong feelings on if the implicit names used by the transformation should be in scope or just accessible via an import. However, I think he makes a convincing argument that it would be pretty weird to require names to be in scope that are not visible anywhere in the source code, so following the choice we already made for ordinary `do` does make sense to me.
-Iavor PS: Arnaud, not that it matters very much, but RankNTypes are not part of Haskell 2010.
On Fri, Apr 10, 2020 at 6:51 AM Richard Eisenberg
wrote: 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 or 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-6061096... 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
wrote: Dear Committe,
Proposal:
https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... 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-6007467... 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-5988592...
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@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

This seems like it could be thought of as a special case of record
selection. (apologies if this has been discussed before, I haven't read the
whole discussion thread.)
The syntax is even reminiscent of record selection. But unlike selecting a
single field, we're selecting a bunch of fields and using them to desugar a
do expression.This makes me wonder: would it be possible to handle the
typing by generating HasField constraints for (>>=) and friends? And would
that avoid needing to talk about "fully settled" types?
Cheers
Simon
On Thu, 9 Apr 2020 at 18:17, Joachim Breitner
Dear Committe,
Proposal:
https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... 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-6007467... 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-5988592...
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@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

On Apr 13, 2020, at 12:40 PM, Simon Marlow
wrote: This seems like it could be thought of as a special case of record selection. (apologies if this has been discussed before, I haven't read the whole discussion thread.)
The syntax is even reminiscent of record selection. But unlike selecting a single field, we're selecting a bunch of fields and using them to desugar a do expression.This makes me wonder: would it be possible to handle the typing by generating HasField constraints for (>>=) and friends? And would that avoid needing to talk about "fully settled" types?
Tempting, but no: the HasField mechanism fails for polymorphic record fields, which we desperately need here. :( Richard
Cheers Simon
On Thu, 9 Apr 2020 at 18:17, Joachim Breitner
mailto:mail@joachim-breitner.de> wrote: Dear Committe, Proposal: https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... Discussion (long, sorry): https://github.com/ghc-proposals/ghc-proposals/pull/216 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-6007467... https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-6007467... 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-5988592... https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-5988592...
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@joachim-breitner.de mailto:mail@joachim-breitner.de http://www.joachim-breitner.de/ http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org mailto:ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hi, Am Montag, den 13.04.2020, 12:40 +0100 schrieb Simon Marlow:
This makes me wonder: would it be possible to handle the typing by generating HasField constraints for (>>=) and friends? And would that avoid needing to talk about "fully settled" types?
this was actually the original proposal by Arnaud from last spring, and if HasField would play well with polymorphic record fields, then we would have just happily accepted it back then… But because of the issues around polymorphic fields, we sent it back, and have now two “work-arounds” (introduce fully settled types, or use the module system) to choose from. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Dear Committe, trying to summarize the discussion here. The main decision seems to be: Record based or module based. Record based is preferred by: Arnaud (one of the authors), SPJ Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based approach is on Simon and Arnaud? Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser) I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that import qualified Monad as M (i.e. _with_ qualification, but _without_ an import list) will give the developer a good user experience, and if they try to do things like import Monad as M (runMonad) … M.do … then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above. We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Dear Committee, it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote? Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one? Cheers, Joachim Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner:
Dear Committe,
trying to summarize the discussion here.
The main decision seems to be: Record based or module based.
Record based is preferred by: Arnaud (one of the authors), SPJ
Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me
This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based approach is on Simon and Arnaud?
Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser)
I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that
import qualified Monad as M
(i.e. _with_ qualification, but _without_ an import list) will give the developer a good user experience, and if they try to do things like
import Monad as M (runMonad) … M.do …
then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above.
We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

I greatly prefer the earlier module-based version of this where the
definitions of the relevant functions are simply obtained from the
qualified module and we don't have to talk about fully-settled types
to explain how expressions are going to desugar. Even if it's not
quite true in the case of GHC's internals that desugaring comes
entirely before typechecking, I think it's really very helpful in both
understanding the language ourselves and in explaining it to beginners
if we can at least think about it that way. The more that the
translation from surface syntax to core factors into a bunch of
individually comprehensible translations, the better.
On Sat, 2 May 2020 at 05:39, Joachim Breitner
Dear Committee,
it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote?
Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one?
Cheers, Joachim
Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner:
Dear Committe,
trying to summarize the discussion here.
The main decision seems to be: Record based or module based.
Record based is preferred by: Arnaud (one of the authors), SPJ
Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me
This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based approach is on Simon and Arnaud?
Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser)
I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that
import qualified Monad as M
(i.e. _with_ qualification, but _without_ an import list) will give the developer a good user experience, and if they try to do things like
import Monad as M (runMonad) … M.do …
then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above.
We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

I’ve been quiet so far because I think both outcomes are perfectly sensible, and I don’t think I have anything to add that hasn’t already been covered. If really pushed, I’d perhaps have a slight bias towards the record-based approach, but really only because of the future possibility of dynamically constructing builders. However, I don’t think that preference is strong enough to be worth counting - I’m happy to abstain. Thanks, Tom
On 2 May 2020, at 10:54, Cale Gibbard
wrote: I greatly prefer the earlier module-based version of this where the definitions of the relevant functions are simply obtained from the qualified module and we don't have to talk about fully-settled types to explain how expressions are going to desugar. Even if it's not quite true in the case of GHC's internals that desugaring comes entirely before typechecking, I think it's really very helpful in both understanding the language ourselves and in explaining it to beginners if we can at least think about it that way. The more that the translation from surface syntax to core factors into a bunch of individually comprehensible translations, the better.
On Sat, 2 May 2020 at 05:39, Joachim Breitner
wrote: Dear Committee,
it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote?
Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one?
Cheers, Joachim
Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner:
Dear Committe,
trying to summarize the discussion here.
The main decision seems to be: Record based or module based.
Record based is preferred by: Arnaud (one of the authors), SPJ
Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me
This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based approach is on Simon and Arnaud?
Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser)
I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that
import qualified Monad as M
(i.e. _with_ qualification, but _without_ an import list) will give the developer a good user experience, and if they try to do things like
import Monad as M (runMonad) … M.do …
then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above.
We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

You can define dynamic builders with the module based approach, there's an
example on the GitHub thread.
Iavor
On Mon, May 4, 2020, 05:00 Tom Harding
I’ve been quiet so far because I think both outcomes are perfectly sensible, and I don’t think I have anything to add that hasn’t already been covered.
If really pushed, I’d perhaps have a slight bias towards the record-based approach, but really only because of the future possibility of dynamically constructing builders. However, I don’t think that preference is strong enough to be worth counting - I’m happy to abstain.
Thanks, Tom
On 2 May 2020, at 10:54, Cale Gibbard
wrote: I greatly prefer the earlier module-based version of this where the definitions of the relevant functions are simply obtained from the qualified module and we don't have to talk about fully-settled types to explain how expressions are going to desugar. Even if it's not quite true in the case of GHC's internals that desugaring comes entirely before typechecking, I think it's really very helpful in both understanding the language ourselves and in explaining it to beginners if we can at least think about it that way. The more that the translation from surface syntax to core factors into a bunch of individually comprehensible translations, the better.
On Sat, 2 May 2020 at 05:39, Joachim Breitner
wrote: Dear Committee,
it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote?
Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one?
Cheers, Joachim
Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner:
Dear Committe,
trying to summarize the discussion here.
The main decision seems to be: Record based or module based.
Record based is preferred by: Arnaud (one of the authors), SPJ
Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me
This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based approach is on Simon and Arnaud?
Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser)
I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that
import qualified Monad as M
(i.e. _with_ qualification, but _without_ an import list) will give the developer a good user experience, and if they try to do things like
import Monad as M (runMonad) … M.do …
then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above.
We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

I’m nervous to endorse any solution that requires implicit parameters generally, just because of the cognitive overhead involved in reading and understanding any code involving them. I think, for the particular problem of dynamic builders, the record approach ends up being more “idiomatic Haskell” (for my personal definition of idiomatic, of course).
That said, I can’t deny that the solution works! Moreover, given that I don’t have a concrete example of a problem in mind, I wouldn’t want to hold up a final decision.
Cheers,
Tom
On 4 May 2020, at 16:22, Iavor Diatchki
On 2 May 2020, at 10:54, Cale Gibbard
mailto:cgibbard@gmail.com> wrote: I greatly prefer the earlier module-based version of this where the definitions of the relevant functions are simply obtained from the qualified module and we don't have to talk about fully-settled types to explain how expressions are going to desugar. Even if it's not quite true in the case of GHC's internals that desugaring comes entirely before typechecking, I think it's really very helpful in both understanding the language ourselves and in explaining it to beginners if we can at least think about it that way. The more that the translation from surface syntax to core factors into a bunch of individually comprehensible translations, the better.
On Sat, 2 May 2020 at 05:39, Joachim Breitner
mailto:mail@joachim-breitner.de> wrote: Dear Committee,
it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote?
Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one?
Cheers, Joachim
Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner:
Dear Committe,
trying to summarize the discussion here.
The main decision seems to be: Record based or module based.
Record based is preferred by: Arnaud (one of the authors), SPJ
Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me
This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based approach is on Simon and Arnaud?
Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser)
I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that
import qualified Monad as M
(i.e. _with_ qualification, but _without_ an import list) will give the developer a good user experience, and if they try to do things like
import Monad as M (runMonad) … M.do …
then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above.
We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.demailto:mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.orgmailto:ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.orgmailto:ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.orgmailto:ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Sorry to have been quiet after Joachim's soliciting more input from me,
Let me try to summarize why I prefer the records approach (and I think it
sort of boils down to the same intuition as Simon (Peyton Jones)'s reasons):
It boils down to: a record would define, in no ambiguous terms, a do
notation desugaring strategy. Whereas in the module-based approach, a given
do block is allowed to pick and mix different combinators from wherever it
wants; even if they don't go together very well.
- This makes it certain which combinators are working together. With the
modules, who's to say that I am not mistakenly taking `(>>)` from a
different location because I'm mixing modules together (and the module I
think I'm using doesn't actually export `(>>)`)? I'd eventually find out,
but it would be rather unpleasant to debug considering that I'm not even
seeing `M.>>` in my code.
- Error messages can tell me: “your builder doesn't support `>>`”, rather
than telling me, “You're not importing an `M.>>`” which is a less ambiguous
error.
This feature, overall, makes more sense to me with records.
On Mon, May 4, 2020 at 6:46 PM Tom Harding
I’m nervous to endorse any solution that requires implicit parameters generally, just because of the cognitive overhead involved in reading and understanding any code involving them. I think, for the particular problem of dynamic builders, the record approach ends up being more “idiomatic Haskell” (for my personal definition of idiomatic, of course).
That said, I can’t deny that the solution works! Moreover, given that I don’t have a concrete example of a problem in mind, I wouldn’t want to hold up a final decision.
Cheers, Tom
On 4 May 2020, at 16:22, Iavor Diatchki
wrote: You can define dynamic builders with the module based approach, there's an example on the GitHub thread.
Iavor
On Mon, May 4, 2020, 05:00 Tom Harding
wrote: I’ve been quiet so far because I think both outcomes are perfectly sensible, and I don’t think I have anything to add that hasn’t already been covered.
If really pushed, I’d perhaps have a slight bias towards the record-based approach, but really only because of the future possibility of dynamically constructing builders. However, I don’t think that preference is strong enough to be worth counting - I’m happy to abstain.
Thanks, Tom
On 2 May 2020, at 10:54, Cale Gibbard
wrote: I greatly prefer the earlier module-based version of this where the definitions of the relevant functions are simply obtained from the qualified module and we don't have to talk about fully-settled types to explain how expressions are going to desugar. Even if it's not quite true in the case of GHC's internals that desugaring comes entirely before typechecking, I think it's really very helpful in both understanding the language ourselves and in explaining it to beginners if we can at least think about it that way. The more that the translation from surface syntax to core factors into a bunch of individually comprehensible translations, the better.
On Sat, 2 May 2020 at 05:39, Joachim Breitner
wrote: Dear Committee,
it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote?
Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one?
Cheers, Joachim
Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner:
Dear Committe,
trying to summarize the discussion here.
The main decision seems to be: Record based or module based.
Record based is preferred by: Arnaud (one of the authors), SPJ
Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me
This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based
is on Simon and Arnaud?
Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser)
I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that
import qualified Monad as M
(i.e. _with_ qualification, but _without_ an import list) will give
approach the
developer a good user experience, and if they try to do things like
import Monad as M (runMonad) … M.do …
then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above.
We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hi, let me try to kindly rebut your points to some extent. 1. Most criticism about the module-based approach boils down to “What if people alias unrelated modules to the same name?” Both in the discussion on visibility (export, or also import) and in this case. I’d say: Nobody aliases unrelated module! (And if some do, I don’t think they deserve to be the main deciding factor in this decision.) 2. Error messages come up too. But again, I don’t see any reason why GHC can’t give equally well error messages in both cases. Note that the desugaring _conceptually_ can happen in parsing, but the implementation certainly won’t, and thus can give informed error messages. So you’d either get maybe You have qualified the do block in … with Foo.builder, but Foo.builder is of type Foo.Builder and the record Builder does not have a field named (>>). vs. You have qualified the do block in … with Foo, but the module Foo does not export a value named (>>). Or, in case you are not importing enough. You have qualified the do block in … with Foo.builder, but you did not import the field accessor Foo.(>>). Did you want to add (>>) to the import list in …. vs. You have qualified the do block in … with Foo, and Foo exports (>>), but you did not import Foo.(>>). Did you want to add (>>) to the import list in …. (or many variations of this, including helpful wording in case the module name aliases multiple modules). BTW, your example seemd to compares the error conditions “builder does not provide (>>)” with “user forgot to import (>>)” – two separate error conditions, that can both occur in both proposals. Neither of these arguments refute your underlying preference for records (which I would absolutely share – if we didn't need this ad-hoc “fully settled” and odd “any type works as long as it has the right fields”). I think it boils down to whether the goal (records) justify the kludges (fully settled, a desugaring that looks up some constructor K withoutusing it). Cheers, Joachim (Can someone maybe just make GetField work with polytypes? Then we woudn’t have any of this discussion, I guess.) Am Dienstag, den 05.05.2020, 08:03 +0200 schrieb Spiwack, Arnaud:
Sorry to have been quiet after Joachim's soliciting more input from me,
Let me try to summarize why I prefer the records approach (and I think it sort of boils down to the same intuition as Simon (Peyton Jones)'s reasons):
It boils down to: a record would define, in no ambiguous terms, a do notation desugaring strategy. Whereas in the module-based approach, a given do block is allowed to pick and mix different combinators from wherever it wants; even if they don't go together very well.
- This makes it certain which combinators are working together. With the modules, who's to say that I am not mistakenly taking `(>>)` from a different location because I'm mixing modules together (and the module I think I'm using doesn't actually export `(>>)`)? I'd eventually find out, but it would be rather unpleasant to debug considering that I'm not even seeing `M.>>` in my code. - Error messages can tell me: “your builder doesn't support `>>`”, rather than telling me, “You're not importing an `M.>>`” which is a less ambiguous error.
This feature, overall, makes more sense to me with records.
On Mon, May 4, 2020 at 6:46 PM Tom Harding
wrote: I’m nervous to endorse any solution that requires implicit parameters generally, just because of the cognitive overhead involved in reading and understanding any code involving them. I think, for the particular problem of dynamic builders, the record approach ends up being more “idiomatic Haskell” (for my personal definition of idiomatic, of course).
That said, I can’t deny that the solution works! Moreover, given that I don’t have a concrete example of a problem in mind, I wouldn’t want to hold up a final decision.
Cheers, Tom
On 4 May 2020, at 16:22, Iavor Diatchki
wrote: You can define dynamic builders with the module based approach, there's an example on the GitHub thread.
Iavor
On Mon, May 4, 2020, 05:00 Tom Harding
wrote: I’ve been quiet so far because I think both outcomes are perfectly sensible, and I don’t think I have anything to add that hasn’t already been covered.
If really pushed, I’d perhaps have a slight bias towards the record-based approach, but really only because of the future possibility of dynamically constructing builders. However, I don’t think that preference is strong enough to be worth counting - I’m happy to abstain.
Thanks, Tom
On 2 May 2020, at 10:54, Cale Gibbard
wrote: I greatly prefer the earlier module-based version of this where the definitions of the relevant functions are simply obtained from the qualified module and we don't have to talk about fully-settled types to explain how expressions are going to desugar. Even if it's not quite true in the case of GHC's internals that desugaring comes entirely before typechecking, I think it's really very helpful in both understanding the language ourselves and in explaining it to beginners if we can at least think about it that way. The more that the translation from surface syntax to core factors into a bunch of individually comprehensible translations, the better.
On Sat, 2 May 2020 at 05:39, Joachim Breitner
wrote: Dear Committee,
it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote?
Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one?
Cheers, Joachim
Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner: > Dear Committe, > > trying to summarize the discussion here. > > The main decision seems to be: Record based or module based. > > Record based is preferred by: > Arnaud (one of the authors), SPJ > > Module based is preferred by: > Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, > me > > This is not a vote, merely a summary of sentimenss. But it seems that > maybe the onus to make stronger arguments for the record-based approach > is on Simon and Arnaud? > > > Related to the module based variant, there was some discussion about > whether the > * desugaring should go to a “qualified name”, that is then resolved > like any manually written name > (i.e. could conceptually be done in the parser) > * or if it should go directly a suitable “original name” of a suitable > function, even if not explicitly imported > (i.e. could conceptually be done in the renamer, but not the parser) > > I initially advocated for the latter, arguing with a “you shall not > have to import stuff you do not write explicitly” rule. > But Simon’s recent argument that > > import qualified Monad as M > > (i.e. _with_ qualification, but _without_ an import list) will give the > developer a good user experience, and if they try to do things like > > import Monad as M (runMonad) > … M.do … > > then, well, they shouldn’t. So I am happy to advocate for the module > based approach with the “must be in scope” rule. But maybe we should > keep the focus more on the fundamental question above. > > > > We could keep in mind that eventually, people will ask for M.if; M.[ > and other “qualified syntax”. But it seems that that will work equally > well with either choice here. > > Cheers, > Joachim > > -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

2. Error messages come up too.
[…]
So you’d either get maybe
You have qualified the do block in … with Foo.builder, but Foo.builder is of type Foo.Builder and the record Builder does not have a field named (>>).
vs.
You have qualified the do block in … with Foo, but the module Foo does not export a value named (>>).
I want to stress that these, if they read as just as good English sentences, don't mean the same thing. The former says: you are using a construction, in your do notation, that your builder doesn't support. The latter says: you haven't imported the module which export this construction, which may or may not exist. Let me make up an example. It is not the case in `base`, but let's imagine that `MonadFail` ins in a different module than `Monad`, then would have to import `Control.Monad.Fail` in addition to `Control.Monad` in order to be able to use partial pattern matching. You may argue that it is bad API design. Which would be fair, but it is hard to assume that such an event can't occur, when designing the compiler. Neither of these arguments refute your underlying preference for
records (which I would absolutely share – if we didn't need this ad-hoc “fully settled” and odd “any type works as long as it has the right fields”).
I think it boils down to whether the goal (records) justify the kludges (fully settled, a desugaring that looks up some constructor K withoutusing it).
It's also a question of whether one would consider these as kludgy. Or whether they sound rather natural to your ears. To me: rather natural, evidently. To you, and most other members of the committee, as far as I could gather, they seem to sound weird and somewhat repulsive.
(Can someone maybe just make GetField work with polytypes? Then we woudn’t have any of this discussion, I guess.)
Cheers to that :-)

I have finally devoted some time to thinking about this properly.
TL;DR: I have made my peace with the module-qualified version.
I agree with Arnaud’s points – I have always wanted to group the operations of the builder together – but the module-qualified version is so easy to explain, understand, and implement, that I think it wins.
For me the other alternative would be to do nothing, and wait for a better idea to come along. E.g. as the proposal points out, we may have other reasons to want fully settled types. But it is really, really attractive to overload the do-notation for other strange monads.
My only real anxiety is that we really will think of a better plan in a few years, and then be stuck with back-compat stuff of code that uses M.do. But maybe we should jump that bridge if we come to it.
Simon
From: ghc-steering-committee

Hi, glad that are converging! Arnaud, can you live with this too? If you do, then I’ll announce that we have accepted the proposal in the variant “6.1”, i.e. https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... without any strange special handling of scoping rules (i.e. not 6.1.2). I still slightly prefer them, but they were contentious, and should we later learn that users really want them, we can add them in a backward- compatible way, so no need to debate that contentious point now. Cheers, Joachim Am Mittwoch, den 06.05.2020, 09:08 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I have finally devoted some time to thinking about this properly.
TL;DR: I have made my peace with the module-qualified version.
I agree with Arnaud’s points – I have always wanted to group the operations of the builder together – but the module-qualified version is so easy to explain, understand, and implement, that I think it wins.
For me the other alternative would be to do nothing, and wait for a better idea to come along. E.g. as the proposal points out, we may have other reasons to want fully settled types. But it is really, really attractive to overload the do-notation for other strange monads.
My only real anxiety is that we really will think of a better plan in a few years, and then be stuck with back-compat stuff of code that uses M.do. But maybe we should jump that bridge if we come to it.
Simon
From: ghc-steering-committee
On Behalf Of Spiwack, Arnaud Sent: 05 May 2020 09:32 To: Joachim Breitner Cc: ]Ghc steering committee Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative 2. Error messages come up too.
[…]
So you’d either get maybe
You have qualified the do block in … with Foo.builder, but Foo.builder is of type Foo.Builder and the record Builder does not have a field named (>>).
vs.
You have qualified the do block in … with Foo, but the module Foo does not export a value named (>>).
I want to stress that these, if they read as just as good English sentences, don't mean the same thing. The former says: you are using a construction, in your do notation, that your builder doesn't support. The latter says: you haven't imported the module which export this construction, which may or may not exist.
Let me make up an example. It is not the case in `base`, but let's imagine that `MonadFail` ins in a different module than `Monad`, then would have to import `Control.Monad.Fail` in addition to `Control.Monad` in order to be able to use partial pattern matching. You may argue that it is bad API design. Which would be fair, but it is hard to assume that such an event can't occur, when designing the compiler.
Neither of these arguments refute your underlying preference for records (which I would absolutely share – if we didn't need this ad-hoc “fully settled” and odd “any type works as long as it has the right fields”).
I think it boils down to whether the goal (records) justify the kludges (fully settled, a desugaring that looks up some constructor K withoutusing it).
It's also a question of whether one would consider these as kludgy. Or whether they sound rather natural to your ears. To me: rather natural, evidently. To you, and most other members of the committee, as far as I could gather, they seem to sound weird and somewhat repulsive.
(Can someone maybe just make GetField work with polytypes? Then we woudn’t have any of this discussion, I guess.)
Cheers to that :-) _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

I can certainly live with the module-based version.
There is one question to solve: should we use the standard names `(>>=)`,
`(>>)` for desugaring? (so that the type class methods can be used
directly). Or some dedicated names `desugaringBind`, … ? To avoid name
clashes.
On Wed, May 6, 2020 at 3:51 PM Joachim Breitner
Hi,
glad that are converging! Arnaud, can you live with this too?
If you do, then I’ll announce that we have accepted the proposal in the variant “6.1”, i.e.
https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... without any strange special handling of scoping rules (i.e. not 6.1.2).
I still slightly prefer them, but they were contentious, and should we later learn that users really want them, we can add them in a backward- compatible way, so no need to debate that contentious point now.
Cheers, Joachim
Am Mittwoch, den 06.05.2020, 09:08 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I have finally devoted some time to thinking about this properly.
TL;DR: I have made my peace with the module-qualified version.
I agree with Arnaud’s points – I have always wanted to group the operations of the builder together – but the module-qualified version is so easy to explain, understand, and implement, that I think it wins.
For me the other alternative would be to do nothing, and wait for a better idea to come along. E.g. as the proposal points out, we may have other reasons to want fully settled types. But it is really, really attractive to overload the do-notation for other strange monads.
My only real anxiety is that we really will think of a better plan in a few years, and then be stuck with back-compat stuff of code that uses M.do. But maybe we should jump that bridge if we come to it.
Simon
From: ghc-steering-committee
On Behalf Of Spiwack, Arnaud Sent: 05 May 2020 09:32 To: Joachim Breitner Cc: ]Ghc steering committee Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative 2. Error messages come up too.
[…]
So you’d either get maybe
You have qualified the do block in … with Foo.builder, but Foo.builder is of type Foo.Builder and the record Builder does not have a field named (>>).
vs.
You have qualified the do block in … with Foo, but the module Foo does not export a value named (>>).
I want to stress that these, if they read as just as good English sentences, don't mean the same thing. The former says: you are using a construction, in your do notation, that your builder doesn't support. The latter says: you haven't imported the module which export this construction, which may or may not exist.
Let me make up an example. It is not the case in `base`, but let's imagine that `MonadFail` ins in a different module than `Monad`, then would have to import `Control.Monad.Fail` in addition to `Control.Monad` in order to be able to use partial pattern matching. You may argue that it is bad API design. Which would be fair, but it is hard to assume that such an event can't occur, when designing the compiler.
Neither of these arguments refute your underlying preference for records (which I would absolutely share – if we didn't need this ad-hoc “fully settled” and odd “any type works as long as it has the right fields”).
I think it boils down to whether the goal (records) justify the kludges (fully settled, a desugaring that looks up some constructor K withoutusing it).
It's also a question of whether one would consider these as kludgy. Or whether they sound rather natural to your ears. To me: rather natural, evidently. To you, and most other members of the committee, as far as I could gather, they seem to sound weird and somewhat repulsive.
(Can someone maybe just make GetField work with polytypes? Then we woudn’t have any of this discussion, I guess.)
Cheers to that :-) _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hi, Am Mittwoch, den 06.05.2020, 15:55 +0200 schrieb Spiwack, Arnaud:
There is one question to solve: should we use the standard names `(>>=)`, `(>>)` for desugaring? (so that the type class methods can be used directly). Or some dedicated names `desugaringBind`, … ? To avoid name clashes.
given that the recommended idiom is to only use this with a qualified module name, I think using the normal, well-known names is reasonable. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Hi, Am Mittwoch, den 06.05.2020, 16:28 +0200 schrieb Joachim Breitner:
Hi,
Am Mittwoch, den 06.05.2020, 15:55 +0200 schrieb Spiwack, Arnaud:
There is one question to solve: should we use the standard names `(>>=)`, `(>>)` for desugaring? (so that the type class methods can be used directly). Or some dedicated names `desugaringBind`, … ? To avoid name clashes.
given that the recommended idiom is to only use this with a qualified module name, I think using the normal, well-known names is reasonable.
do we have more opinions on this? If not we can go with the author’s proposal, which is to use the standard names. It’s natural that when I can write `M.do { a M.>> b ; c }` after all, and helpful if programmer can expect M.>> to be there for every module M that they would use to qualify `do`. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Hmm. I can see the attraction of using other less widely used names like
M.qualifiedDoBind
M.qualifiedDoFail
M.qualifedDoMFix
Then searching for `qualifiedDo` would find the things that are used for, well qualified do. Remember, by using the module-prefix form we are making it harder to group together the operations that make up the things used by qualified do -- (>>), (>>=) etc are among the most widely used lexemes in Haskell.
I'm not immovable on this, but I think there's a case for using long, explicit names. If you end up saying
qualifiedDoBind = (>>=)
that would be a very good clue that you intend this operation to be used by Qualified Do.
Simon
| -----Original Message-----
| From: ghc-steering-committee

Hmm indeed :-) Both ways are justifiable. Allow me to explain why using the normal operations seem to be a better trade off. Here is an hypothesis: Most people using do-notation throw in some (non-sugar) monad operations from time to time, e.g.: do doFoo z <- bar x >>= baz >>= quux return (f z) This will not change if they use qualifed monads, so we better allow them to write M.do doFoo z <- bar x M.>>= baz M.>>= quux M.return (f z) So I expect that any module M that is set up to be used qualified will want to export (>>=) and (>>) and return anyways. This observation does not require us to use these in the desugaring. But since they are there, it seems natural to use them. If we don’t, then * some modules export qualifiedDoBind and (>>=) which is redundant (and makes one wonder if they are the same) * a few modules might not export (>>=) (only qualifiedDoBind) for whatever reason This adds cognitive load on the user, who now has to check and remember for which M.do they also can use M.>>= I acknowledge the downside that such a module M may be less easy to recognize as “supports qualified do” just given the list of exports. (Some may call that a feature, e.g. Prelude.do would just work…) I hope that a good haddock module header can address that, maybe by having a section “Used of the Linear monad with QualifiedDo” or such. Cheers, Joachim Am Freitag, den 08.05.2020, 22:40 +0000 schrieb Simon Peyton Jones:
Hmm. I can see the attraction of using other less widely used names like M.qualifiedDoBind M.qualifiedDoFail M.qualifedDoMFix
Then searching for `qualifiedDo` would find the things that are used for, well qualified do. Remember, by using the module-prefix form we are making it harder to group together the operations that make up the things used by qualified do -- (>>), (>>=) etc are among the most widely used lexemes in Haskell.
I'm not immovable on this, but I think there's a case for using long, explicit names. If you end up saying qualifiedDoBind = (>>=) that would be a very good clue that you intend this operation to be used by Qualified Do.
Simon
-----Original Message----- From: ghc-steering-committee
On Behalf Of Joachim Breitner Sent: 08 May 2020 23:02 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative Hi,
Am Mittwoch, den 06.05.2020, 16:28 +0200 schrieb Joachim Breitner:
Hi,
Am Mittwoch, den 06.05.2020, 15:55 +0200 schrieb Spiwack, Arnaud:
There is one question to solve: should we use the standard names `(>>=)`, `(>>)` for desugaring? (so that the type class methods can be used directly). Or some dedicated names `desugaringBind`, … ? To avoid name clashes.
given that the recommended idiom is to only use this with a qualified module name, I think using the normal, well-known names is reasonable.
do we have more opinions on this? If not we can go with the author’s proposal, which is to use the standard names. It’s natural that when I can write `M.do { a M.>> b ; c }` after all, and helpful if programmer can expect M.>> to be there for every module M that they would use to qualify `do`.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

I am with Joachim in the use of "normal" names as opposed to special
"qualifiedX". At least in my case, I tend to use >>= and >> here and there
in my monadic code, so having them exported from the same module would be
desirable. In fact, I would expect those modules to also export something
akin to <$> and <*>.
Alejandro
El sáb., 9 may. 2020 a las 0:58, Joachim Breitner (
Hmm indeed :-)
Both ways are justifiable. Allow me to explain why using the normal operations seem to be a better trade off.
Here is an hypothesis:
Most people using do-notation throw in some (non-sugar) monad operations from time to time, e.g.:
do doFoo z <- bar x >>= baz >>= quux return (f z)
This will not change if they use qualifed monads, so we better allow them to write
M.do doFoo z <- bar x M.>>= baz M.>>= quux M.return (f z)
So I expect that any module M that is set up to be used qualified will want to export (>>=) and (>>) and return anyways.
This observation does not require us to use these in the desugaring. But since they are there, it seems natural to use them.
If we don’t, then * some modules export qualifiedDoBind and (>>=) which is redundant (and makes one wonder if they are the same) * a few modules might not export (>>=) (only qualifiedDoBind) for whatever reason This adds cognitive load on the user, who now has to check and remember for which M.do they also can use M.>>=
I acknowledge the downside that such a module M may be less easy to recognize as “supports qualified do” just given the list of exports. (Some may call that a feature, e.g. Prelude.do would just work…) I hope that a good haddock module header can address that, maybe by having a section “Used of the Linear monad with QualifiedDo” or such.
Cheers, Joachim
Am Freitag, den 08.05.2020, 22:40 +0000 schrieb Simon Peyton Jones:
Hmm. I can see the attraction of using other less widely used names like M.qualifiedDoBind M.qualifiedDoFail M.qualifedDoMFix
Then searching for `qualifiedDo` would find the things that are used for, well qualified do. Remember, by using the module-prefix form we are making it harder to group together the operations that make up the things used by qualified do -- (>>), (>>=) etc are among the most widely used lexemes in Haskell.
I'm not immovable on this, but I think there's a case for using long, explicit names. If you end up saying qualifiedDoBind = (>>=) that would be a very good clue that you intend this operation to be used by Qualified Do.
Simon
-----Original Message----- From: ghc-steering-committee < ghc-steering-committee-bounces@haskell.org> On Behalf Of Joachim Breitner Sent: 08 May 2020 23:02 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative
Hi,
Am Mittwoch, den 06.05.2020, 16:28 +0200 schrieb Joachim Breitner:
Hi,
Am Mittwoch, den 06.05.2020, 15:55 +0200 schrieb Spiwack, Arnaud:
There is one question to solve: should we use the standard names `(>>=)`, `(>>)` for desugaring? (so that the type class methods can be used directly). Or some dedicated names `desugaringBind`, … ? To avoid name clashes.
given that the recommended idiom is to only use this with a qualified module name, I think using the normal, well-known names is reasonable.
do we have more opinions on this? If not we can go with the author’s proposal, which is to use the standard names. It’s natural that when I can write `M.do { a M.>> b ; c }` after all, and helpful if programmer can expect M.>> to be there for every module M that they would use to qualify `do`.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

I agree that having longer names would mean that you may have to say
module M( (>>=), (>>), fail
, qualifiedBind, qualifiedThen, qualifiedFail ) where
qualifiedBind = (>>=)
qualifiedThen = (>>)
qualifiedFail = fail
…defns of (>>=) etc…
but you could regard those extra lines as a feature, rather than a bug. They say “I’m planning to use these operators for M.do”. I like that. And I like being able to grep for things in a large codebase. (Grepping for (>>=) is useless.)
But I can see that judgements might vary.
Simon
From: ghc-steering-committee
Hmm. I can see the attraction of using other less widely used names like M.qualifiedDoBind M.qualifiedDoFail M.qualifedDoMFix
Then searching for `qualifiedDo` would find the things that are used for, well qualified do. Remember, by using the module-prefix form we are making it harder to group together the operations that make up the things used by qualified do -- (>>), (>>=) etc are among the most widely used lexemes in Haskell.
I'm not immovable on this, but I think there's a case for using long, explicit names. If you end up saying qualifiedDoBind = (>>=) that would be a very good clue that you intend this operation to be used by Qualified Do.
Simon
-----Original Message----- From: ghc-steering-committee
mailto:ghc-steering-committee-bounces@haskell.org> On Behalf Of Joachim Breitner Sent: 08 May 2020 23:02 To: ghc-steering-committee@haskell.orgmailto:ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative Hi,
Am Mittwoch, den 06.05.2020, 16:28 +0200 schrieb Joachim Breitner:
Hi,
Am Mittwoch, den 06.05.2020, 15:55 +0200 schrieb Spiwack, Arnaud:
There is one question to solve: should we use the standard names `(>>=)`, `(>>)` for desugaring? (so that the type class methods can be used directly). Or some dedicated names `desugaringBind`, … ? To avoid name clashes.
given that the recommended idiom is to only use this with a qualified module name, I think using the normal, well-known names is reasonable.
do we have more opinions on this? If not we can go with the author’s proposal, which is to use the standard names. It’s natural that when I can write `M.do { a M.>> b ; c }` after all, and helpful if programmer can expect M.>> to be there for every module M that they would use to qualify `do`.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.demailto:mail@joachim-breitner.de http://www.joachim-breitner.de/https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joachim-breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894365128&sdata=aVA%2BFFcNViULzX%2B1awe8oCkE74avIhz50jyOup0tYAs%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.orgmailto:ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committeehttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering-committee&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894375125&sdata=AAJ3WkF%2BjQWpQcHeIA3kwF2hU4kjsqKC9kKffqf7iyo%3D&reserved=0 -- Joachim Breitner mail@joachim-breitner.demailto:mail@joachim-breitner.de http://www.joachim-breitner.de/https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joachim-breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894375125&sdata=0VFHs12K4Co7BCqK8Byme%2BIaCZBSLJuc%2BIrjgm57LVA%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.orgmailto:ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committeehttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering-committee&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894385121&sdata=t3edpQsri4nEzJyY92aOQrDY%2F7yJ2EVgavpc%2FKF1yws%3D&reserved=0

Hi Am Montag, den 11.05.2020, 08:12 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
But I can see that judgements might vary.
I can also live with either way. If that’s true for all of us, how about we let the proposal author decide on this small point? This way we don't have to come to agreement, and the authors get back some sense of ownership of this proposal, after all the back-and-forth with the comittee? I’ll send them link to the relevant posts by you and me to highlight the advantages of each variant. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

On May 11, 2020, at 1:12 PM, Joachim Breitner
wrote: how about we let the proposal author decide on this small point? This way we don't have to come to agreement, and the authors get back some sense of ownership of this proposal, after all the back-and-forth with the comittee?
How delightfully diplomatic. Yes, please. Richard

El lun., 11 may. 2020 a las 15:20, Richard Eisenberg (
On May 11, 2020, at 1:12 PM, Joachim Breitner
wrote: how about we let the proposal author decide on this small point? This way we don't have to come to agreement, and the authors get back some sense of ownership of this proposal, after all the back-and-forth with the comittee?
How delightfully diplomatic. Yes, please.
I couldn't have expressed it better. I think Joachim's proposal is perfect :)

Hi, Am Montag, den 11.05.2020, 14:20 +0100 schrieb Richard Eisenberg:
How delightfully diplomatic. Yes, please.
thanks! Done: https://github.com/ghc-proposals/ghc-proposals/pull/216#issuecomment-6278643... Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Dear Committee, Facundo Domínguez has updated the proposal to be based on module names again, see https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000-local-do... (with a wealth of alternatives). He opted for desugaring to the well-known name, leaving the “recognizability” of the feature to other means (module names, documentation). Please have a final quick look if this document is fine to approve, and say so. I’ll wait for either explicit nods from people who were involved here, or just wait for a few days of silence, before merging this. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

I think it's good to go now.
Simon
| -----Original Message-----
| From: ghc-steering-committee

Accepted! Phew. Second most-commented proposal done. Am Freitag, den 15.05.2020, 13:54 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I think it's good to go now.
Simon
-----Original Message----- From: ghc-steering-committee
On Behalf Of Joachim Breitner Sent: 15 May 2020 14:53 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do, final(?) version Dear Committee,
Facundo Domínguez has updated the proposal to be based on module names again, see https://github.com/tweag/ghc-proposals/blob/local-do/proposals/0000- local-do.rst (with a wealth of alternatives).
He opted for desugaring to the well-known name, leaving the “recognizability” of the feature to other means (module names, documentation).
Please have a final quick look if this document is fine to approve, and say so. I’ll wait for either explicit nods from people who were involved here, or just wait for a few days of silence, before merging this.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

I really like using the standard names, but greppability is desirable too.
So perhaps a reasonable convention would be
module MyLib.QualifiedDo ( (>>=), (>>), fail ) where { ... }
then
import MyLib.QualifiedDo as MyLib
... MyLib.do { ... }
you could easily grep for modules named `.QualifiedDo`. It would be just a
convention, of course.
Cheers
Simon
On Mon, 11 May 2020 at 09:12, Simon Peyton Jones via ghc-steering-committee
I agree that having longer names would mean that you may have to say
module M( (>>=), (>>), fail
, qualifiedBind, qualifiedThen, qualifiedFail ) where
qualifiedBind = (>>=)
qualifiedThen = (>>)
qualifiedFail = fail
…defns of (>>=) etc…
but you could regard those extra lines as a feature, rather than a bug. They say “I’m planning to use these operators for M.do”. I like that. And I like being able to grep for things in a large codebase. (Grepping for (>>=) is useless.)
But I can see that judgements might vary.
Simon
*From:* ghc-steering-committee
*On Behalf Of *Alejandro Serrano Mena *Sent:* 09 May 2020 20:13 *To:* Joachim Breitner *Cc:* ghc-steering-committee@haskell.org *Subject:* Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative I am with Joachim in the use of "normal" names as opposed to special "qualifiedX". At least in my case, I tend to use >>= and >> here and there in my monadic code, so having them exported from the same module would be desirable. In fact, I would expect those modules to also export something akin to <$> and <*>.
Alejandro
El sáb., 9 may. 2020 a las 0:58, Joachim Breitner (< mail@joachim-breitner.de>) escribió:
Hmm indeed :-)
Both ways are justifiable. Allow me to explain why using the normal operations seem to be a better trade off.
Here is an hypothesis:
Most people using do-notation throw in some (non-sugar) monad operations from time to time, e.g.:
do doFoo z <- bar x >>= baz >>= quux return (f z)
This will not change if they use qualifed monads, so we better allow them to write
M.do doFoo z <- bar x M.>>= baz M.>>= quux M.return (f z)
So I expect that any module M that is set up to be used qualified will want to export (>>=) and (>>) and return anyways.
This observation does not require us to use these in the desugaring. But since they are there, it seems natural to use them.
If we don’t, then * some modules export qualifiedDoBind and (>>=) which is redundant (and makes one wonder if they are the same) * a few modules might not export (>>=) (only qualifiedDoBind) for whatever reason This adds cognitive load on the user, who now has to check and remember for which M.do they also can use M.>>=
I acknowledge the downside that such a module M may be less easy to recognize as “supports qualified do” just given the list of exports. (Some may call that a feature, e.g. Prelude.do would just work…) I hope that a good haddock module header can address that, maybe by having a section “Used of the Linear monad with QualifiedDo” or such.
Cheers, Joachim
Am Freitag, den 08.05.2020, 22:40 +0000 schrieb Simon Peyton Jones:
Hmm. I can see the attraction of using other less widely used names like M.qualifiedDoBind M.qualifiedDoFail M.qualifedDoMFix
Then searching for `qualifiedDo` would find the things that are used for, well qualified do. Remember, by using the module-prefix form we are making it harder to group together the operations that make up the things used by qualified do -- (>>), (>>=) etc are among the most widely used lexemes in Haskell.
I'm not immovable on this, but I think there's a case for using long, explicit names. If you end up saying qualifiedDoBind = (>>=) that would be a very good clue that you intend this operation to be used by Qualified Do.
Simon
-----Original Message----- From: ghc-steering-committee < ghc-steering-committee-bounces@haskell.org> On Behalf Of Joachim Breitner Sent: 08 May 2020 23:02 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative
Hi,
Am Mittwoch, den 06.05.2020, 16:28 +0200 schrieb Joachim Breitner:
Hi,
Am Mittwoch, den 06.05.2020, 15:55 +0200 schrieb Spiwack, Arnaud:
There is one question to solve: should we use the standard names `(>>=)`, `(>>)` for desugaring? (so that the type class methods can be used directly). Or some dedicated names `desugaringBind`, … ? To avoid name clashes.
given that the recommended idiom is to only use this with a qualified module name, I think using the normal, well-known names is reasonable.
do we have more opinions on this? If not we can go with the author’s proposal, which is to use the standard names. It’s natural that when I can write `M.do { a M.>> b ; c }` after all, and helpful if programmer can expect M.>> to be there for every module M that they would use to qualify `do`.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/ https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joachim-breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894365128&sdata=aVA%2BFFcNViULzX%2B1awe8oCkE74avIhz50jyOup0tYAs%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering-committee&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894375125&sdata=AAJ3WkF%2BjQWpQcHeIA3kwF2hU4kjsqKC9kKffqf7iyo%3D&reserved=0 -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/ https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joachim-breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894375125&sdata=0VFHs12K4Co7BCqK8Byme%2BIaCZBSLJuc%2BIrjgm57LVA%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering-committee&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894385121&sdata=t3edpQsri4nEzJyY92aOQrDY%2F7yJ2EVgavpc%2FKF1yws%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

I don't have an opinion on what the operations should be called, and am
happy to leave it to others to decide.
-Iavor
On Tue, May 12, 2020 at 1:32 PM Simon Marlow
I really like using the standard names, but greppability is desirable too. So perhaps a reasonable convention would be
module MyLib.QualifiedDo ( (>>=), (>>), fail ) where { ... }
then
import MyLib.QualifiedDo as MyLib
... MyLib.do { ... }
you could easily grep for modules named `.QualifiedDo`. It would be just a convention, of course.
Cheers Simon
On Mon, 11 May 2020 at 09:12, Simon Peyton Jones via ghc-steering-committee
wrote: I agree that having longer names would mean that you may have to say
module M( (>>=), (>>), fail
, qualifiedBind, qualifiedThen, qualifiedFail ) where
qualifiedBind = (>>=)
qualifiedThen = (>>)
qualifiedFail = fail
…defns of (>>=) etc…
but you could regard those extra lines as a feature, rather than a bug. They say “I’m planning to use these operators for M.do”. I like that. And I like being able to grep for things in a large codebase. (Grepping for (>>=) is useless.)
But I can see that judgements might vary.
Simon
*From:* ghc-steering-committee < ghc-steering-committee-bounces@haskell.org> *On Behalf Of *Alejandro Serrano Mena *Sent:* 09 May 2020 20:13 *To:* Joachim Breitner
*Cc:* ghc-steering-committee@haskell.org *Subject:* Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative I am with Joachim in the use of "normal" names as opposed to special "qualifiedX". At least in my case, I tend to use >>= and >> here and there in my monadic code, so having them exported from the same module would be desirable. In fact, I would expect those modules to also export something akin to <$> and <*>.
Alejandro
El sáb., 9 may. 2020 a las 0:58, Joachim Breitner (< mail@joachim-breitner.de>) escribió:
Hmm indeed :-)
Both ways are justifiable. Allow me to explain why using the normal operations seem to be a better trade off.
Here is an hypothesis:
Most people using do-notation throw in some (non-sugar) monad operations from time to time, e.g.:
do doFoo z <- bar x >>= baz >>= quux return (f z)
This will not change if they use qualifed monads, so we better allow them to write
M.do doFoo z <- bar x M.>>= baz M.>>= quux M.return (f z)
So I expect that any module M that is set up to be used qualified will want to export (>>=) and (>>) and return anyways.
This observation does not require us to use these in the desugaring. But since they are there, it seems natural to use them.
If we don’t, then * some modules export qualifiedDoBind and (>>=) which is redundant (and makes one wonder if they are the same) * a few modules might not export (>>=) (only qualifiedDoBind) for whatever reason This adds cognitive load on the user, who now has to check and remember for which M.do they also can use M.>>=
I acknowledge the downside that such a module M may be less easy to recognize as “supports qualified do” just given the list of exports. (Some may call that a feature, e.g. Prelude.do would just work…) I hope that a good haddock module header can address that, maybe by having a section “Used of the Linear monad with QualifiedDo” or such.
Cheers, Joachim
Hmm. I can see the attraction of using other less widely used names
Am Freitag, den 08.05.2020, 22:40 +0000 schrieb Simon Peyton Jones: like
M.qualifiedDoBind M.qualifiedDoFail M.qualifedDoMFix
Then searching for `qualifiedDo` would find the things that are used for, well qualified do. Remember, by using the module-prefix form we are making it harder to group together the operations that make up the things used by qualified do -- (>>), (>>=) etc are among the most widely used lexemes in Haskell.
I'm not immovable on this, but I think there's a case for using long,
explicit names. If you end up saying
qualifiedDoBind = (>>=) that would be a very good clue that you intend this operation to be
used by Qualified Do.
Simon
-----Original Message----- From: ghc-steering-committee <
ghc-steering-committee-bounces@haskell.org>
On Behalf Of Joachim Breitner Sent: 08 May 2020 23:02 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative
Hi,
Am Mittwoch, den 06.05.2020, 16:28 +0200 schrieb Joachim Breitner:
Hi,
Am Mittwoch, den 06.05.2020, 15:55 +0200 schrieb Spiwack, Arnaud:
There is one question to solve: should we use the standard names `(>>=)`, `(>>)` for desugaring? (so that the type class methods can be used directly). Or some dedicated names `desugaringBind`, … ? To avoid name clashes.
given that the recommended idiom is to only use this with a qualified module name, I think using the normal, well-known names is reasonable.
do we have more opinions on this? If not we can go with the author’s proposal, which is to use the standard names. It’s natural that when I can write `M.do { a M.>> b ; c }` after all, and helpful if programmer can expect M.>> to be there for every module M that they would use to qualify `do`.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/ https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joachim-breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894365128&sdata=aVA%2BFFcNViULzX%2B1awe8oCkE74avIhz50jyOup0tYAs%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering-committee&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894375125&sdata=AAJ3WkF%2BjQWpQcHeIA3kwF2hU4kjsqKC9kKffqf7iyo%3D&reserved=0 -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/ https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joachim-breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894375125&sdata=0VFHs12K4Co7BCqK8Byme%2BIaCZBSLJuc%2BIrjgm57LVA%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering-committee&data=02%7C01%7Csimonpj%40microsoft.com%7Ce9fed952b2ac4d6d94e808d7f44d0197%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637246483894385121&sdata=t3edpQsri4nEzJyY92aOQrDY%2F7yJ2EVgavpc%2FKF1yws%3D&reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

I'd rather see a final edit of the proposal, reflecting the final choices, before formally tying the bow.
We did that with record dot syntax
S
| -----Original Message-----
| From: ghc-steering-committee

Hi, hmm, is that necessary? I think all we are saying is “your previous submission was actually fine”. Nothing has changed since then, as far as I can tell. That version is, I believe, this one: https://github.com/tweag/ghc-proposals/blob/2a1dcc29cc9db7a1f4e86b6cfb86d87c... But we can ping the committee once more, if you think that’s helpful. Cheers, Joachim Am Mittwoch, den 06.05.2020, 14:02 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I'd rather see a final edit of the proposal, reflecting the final choices, before formally tying the bow.
We did that with record dot syntax
S
-----Original Message----- From: ghc-steering-committee
On Behalf Of Joachim Breitner Sent: 06 May 2020 14:52 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative Hi,
glad that are converging! Arnaud, can you live with this too?
If you do, then I’ll announce that we have accepted the proposal in the variant “6.1”, i.e. https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub. com%2Ftweag%2Fghc-proposals%2Fblob%2Flocal-do%2Fproposals%2F0000-local- do.rst%23do-with-a-module- name&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005ce08d 7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637243699193608686 &sdata=BahOAFyD0iEutyMzg0YzKBDtDe%2BTQTkfQak0tQtFLhU%3D&reserved= 0 without any strange special handling of scoping rules (i.e. not 6.1.2).
I still slightly prefer them, but they were contentious, and should we later learn that users really want them, we can add them in a backward- compatible way, so no need to debate that contentious point now.
Cheers, Joachim
Am Mittwoch, den 06.05.2020, 09:08 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I have finally devoted some time to thinking about this properly.
TL;DR: I have made my peace with the module-qualified version.
I agree with Arnaud’s points – I have always wanted to group the operations of the builder together – but the module-qualified version is so easy to explain, understand, and implement, that I think it wins.
For me the other alternative would be to do nothing, and wait for a better idea to come along. E.g. as the proposal points out, we may have other reasons to want fully settled types. But it is really, really attractive to overload the do-notation for other strange monads.
My only real anxiety is that we really will think of a better plan in a few years, and then be stuck with back-compat stuff of code that uses M.do. But maybe we should jump that bridge if we come to it.
Simon
From: ghc-steering-committee
On Behalf Of Spiwack, Arnaud Sent: 05 May 2020 09:32 To: Joachim Breitner Cc: ]Ghc steering committee Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative 2. Error messages come up too.
[…]
So you’d either get maybe
You have qualified the do block in … with Foo.builder, but Foo.builder is of type Foo.Builder and the record Builder does not have a field named (>>).
vs.
You have qualified the do block in … with Foo, but the module Foo does not export a value named (>>).
I want to stress that these, if they read as just as good English sentences, don't mean the same thing. The former says: you are using a construction, in your do notation, that your builder doesn't support. The latter says: you haven't imported the module which export this construction, which may or may not exist.
Let me make up an example. It is not the case in `base`, but let's imagine that `MonadFail` ins in a different module than `Monad`, then would have to import `Control.Monad.Fail` in addition to `Control.Monad` in order to be able to use partial pattern matching. You may argue that it is bad API design. Which would be fair, but it is hard to assume that such an event can't occur, when designing the compiler.
Neither of these arguments refute your underlying preference for records (which I would absolutely share – if we didn't need this ad- hoc “fully settled” and odd “any type works as long as it has the right fields”).
I think it boils down to whether the goal (records) justify the kludges (fully settled, a desugaring that looks up some constructor K withoutusing it).
It's also a question of whether one would consider these as kludgy. Or whether they sound rather natural to your ears. To me: rather natural, evidently. To you, and most other members of the committee, as far as I could gather, they seem to sound weird and somewhat repulsive.
(Can someone maybe just make GetField work with polytypes? Then we woudn’t have any of this discussion, I guess.)
Cheers to that :-) _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.ha skell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering- committee&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005 ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6372436991936 18681&sdata=VoAprdrPQj326%2F7nVXdF0GUk7Y%2BPBAoHM4fBH7w27QE%3D&re served=0 -- Joachim Breitner mail@joachim-breitner.de
https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joac him- breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac514 49005ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C63724369 9193618681&sdata=4a46m7moVg%2BH4dsGr%2F831WHPPQ79cdzfKMDp0wAwoSs%3D&a mp;reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.ha skell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering- committee&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005 ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6372436991936 18681&sdata=VoAprdrPQj326%2F7nVXdF0GUk7Y%2BPBAoHM4fBH7w27QE%3D&re served=0
ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

For me, the key point is that the proposal as merged into `master` is the correct, final proposal. I don't think we need a re-review after that is done, but I wouldn't want to just merge a proposal that's not in its final state. Richard
On May 6, 2020, at 3:30 PM, Joachim Breitner
wrote: Hi,
hmm, is that necessary? I think all we are saying is “your previous submission was actually fine”. Nothing has changed since then, as far as I can tell. That version is, I believe, this one:
https://github.com/tweag/ghc-proposals/blob/2a1dcc29cc9db7a1f4e86b6cfb86d87c...
But we can ping the committee once more, if you think that’s helpful.
Cheers, Joachim
Am Mittwoch, den 06.05.2020, 14:02 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I'd rather see a final edit of the proposal, reflecting the final choices, before formally tying the bow.
We did that with record dot syntax
S
-----Original Message----- From: ghc-steering-committee
On Behalf Of Joachim Breitner Sent: 06 May 2020 14:52 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative Hi,
glad that are converging! Arnaud, can you live with this too?
If you do, then I’ll announce that we have accepted the proposal in the variant “6.1”, i.e. https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub. com%2Ftweag%2Fghc-proposals%2Fblob%2Flocal-do%2Fproposals%2F0000-local- do.rst%23do-with-a-module- name&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005ce08d 7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637243699193608686 &sdata=BahOAFyD0iEutyMzg0YzKBDtDe%2BTQTkfQak0tQtFLhU%3D&reserved= 0 without any strange special handling of scoping rules (i.e. not 6.1.2).
I still slightly prefer them, but they were contentious, and should we later learn that users really want them, we can add them in a backward- compatible way, so no need to debate that contentious point now.
Cheers, Joachim
Am Mittwoch, den 06.05.2020, 09:08 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I have finally devoted some time to thinking about this properly.
TL;DR: I have made my peace with the module-qualified version.
I agree with Arnaud’s points – I have always wanted to group the operations of the builder together – but the module-qualified version is so easy to explain, understand, and implement, that I think it wins.
For me the other alternative would be to do nothing, and wait for a better idea to come along. E.g. as the proposal points out, we may have other reasons to want fully settled types. But it is really, really attractive to overload the do-notation for other strange monads.
My only real anxiety is that we really will think of a better plan in a few years, and then be stuck with back-compat stuff of code that uses M.do. But maybe we should jump that bridge if we come to it.
Simon
From: ghc-steering-committee
On Behalf Of Spiwack, Arnaud Sent: 05 May 2020 09:32 To: Joachim Breitner Cc: ]Ghc steering committee Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative 2. Error messages come up too.
[…]
So you’d either get maybe
You have qualified the do block in … with Foo.builder, but Foo.builder is of type Foo.Builder and the record Builder does not have a field named (>>).
vs.
You have qualified the do block in … with Foo, but the module Foo does not export a value named (>>).
I want to stress that these, if they read as just as good English sentences, don't mean the same thing. The former says: you are using a construction, in your do notation, that your builder doesn't support. The latter says: you haven't imported the module which export this construction, which may or may not exist.
Let me make up an example. It is not the case in `base`, but let's imagine that `MonadFail` ins in a different module than `Monad`, then would have to import `Control.Monad.Fail` in addition to `Control.Monad` in order to be able to use partial pattern matching. You may argue that it is bad API design. Which would be fair, but it is hard to assume that such an event can't occur, when designing the compiler.
Neither of these arguments refute your underlying preference for records (which I would absolutely share – if we didn't need this ad- hoc “fully settled” and odd “any type works as long as it has the right fields”).
I think it boils down to whether the goal (records) justify the kludges (fully settled, a desugaring that looks up some constructor K withoutusing it).
It's also a question of whether one would consider these as kludgy. Or whether they sound rather natural to your ears. To me: rather natural, evidently. To you, and most other members of the committee, as far as I could gather, they seem to sound weird and somewhat repulsive.
(Can someone maybe just make GetField work with polytypes? Then we woudn’t have any of this discussion, I guess.)
Cheers to that :-) _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.ha skell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering- committee&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005 ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6372436991936 18681&sdata=VoAprdrPQj326%2F7nVXdF0GUk7Y%2BPBAoHM4fBH7w27QE%3D&re served=0 -- Joachim Breitner mail@joachim-breitner.de
https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joac him- breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac514 49005ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C63724369 9193618681&sdata=4a46m7moVg%2BH4dsGr%2F831WHPPQ79cdzfKMDp0wAwoSs%3D&a mp;reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.ha skell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering- committee&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005 ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6372436991936 18681&sdata=VoAprdrPQj326%2F7nVXdF0GUk7Y%2BPBAoHM4fBH7w27QE%3D&re served=0
ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee

Hi, anyways, I’ll ask the authors to update the proposal, and notify the committee for a brief glance before it gets merged. Cheers, Joachim Am Mittwoch, den 06.05.2020, 15:56 +0100 schrieb Richard Eisenberg:
For me, the key point is that the proposal as merged into `master` is the correct, final proposal. I don't think we need a re-review after that is done, but I wouldn't want to just merge a proposal that's not in its final state.
Richard
On May 6, 2020, at 3:30 PM, Joachim Breitner
wrote: Hi,
hmm, is that necessary? I think all we are saying is “your previous submission was actually fine”. Nothing has changed since then, as far as I can tell. That version is, I believe, this one:
https://github.com/tweag/ghc-proposals/blob/2a1dcc29cc9db7a1f4e86b6cfb86d87c...
But we can ping the committee once more, if you think that’s helpful.
Cheers, Joachim
Am Mittwoch, den 06.05.2020, 14:02 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I'd rather see a final edit of the proposal, reflecting the final choices, before formally tying the bow.
We did that with record dot syntax
S
-----Original Message----- From: ghc-steering-committee
On Behalf Of Joachim Breitner Sent: 06 May 2020 14:52 To: ghc-steering-committee@haskell.org Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative Hi,
glad that are converging! Arnaud, can you live with this too?
If you do, then I’ll announce that we have accepted the proposal in the variant “6.1”, i.e. https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub. com%2Ftweag%2Fghc-proposals%2Fblob%2Flocal-do%2Fproposals%2F0000-local- do.rst%23do-with-a-module- name&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005ce08d 7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637243699193608686 &sdata=BahOAFyD0iEutyMzg0YzKBDtDe%2BTQTkfQak0tQtFLhU%3D&reserved= 0 without any strange special handling of scoping rules (i.e. not 6.1.2).
I still slightly prefer them, but they were contentious, and should we later learn that users really want them, we can add them in a backward- compatible way, so no need to debate that contentious point now.
Cheers, Joachim
Am Mittwoch, den 06.05.2020, 09:08 +0000 schrieb Simon Peyton Jones via ghc-steering-committee:
I have finally devoted some time to thinking about this properly.
TL;DR: I have made my peace with the module-qualified version.
I agree with Arnaud’s points – I have always wanted to group the operations of the builder together – but the module-qualified version is so easy to explain, understand, and implement, that I think it wins. For me the other alternative would be to do nothing, and wait for a better idea to come along. E.g. as the proposal points out, we may have other reasons to want fully settled types. But it is really, really attractive to overload the do-notation for other strange monads. My only real anxiety is that we really will think of a better plan in a few years, and then be stuck with back-compat stuff of code that uses M.do. But maybe we should jump that bridge if we come to it. Simon
From: ghc-steering-committee
On Behalf Of Spiwack, Arnaud Sent: 05 May 2020 09:32 To: Joachim Breitner Cc: ]Ghc steering committee Subject: Re: [ghc-steering-committee] #216: Qualified Do again, recommendation: accept the alternative 2. Error messages come up too.
[…]
So you’d either get maybe
You have qualified the do block in … with Foo.builder, but Foo.builder is of type Foo.Builder and the record Builder does not have a field named (>>).
vs.
You have qualified the do block in … with Foo, but the module Foo does not export a value named (>>).
I want to stress that these, if they read as just as good English sentences, don't mean the same thing. The former says: you are using a construction, in your do notation, that your builder doesn't support. The latter says: you haven't imported the module which export this construction, which may or may not exist. Let me make up an example. It is not the case in `base`, but let's imagine that `MonadFail` ins in a different module than `Monad`, then would have to import `Control.Monad.Fail` in addition to `Control.Monad` in order to be able to use partial pattern matching. You may argue that it is bad API design. Which would be fair, but it is hard to assume that such an event can't occur, when designing the compiler.
Neither of these arguments refute your underlying preference for records (which I would absolutely share – if we didn't need this ad- hoc “fully settled” and odd “any type works as long as it has the right fields”).
I think it boils down to whether the goal (records) justify the kludges (fully settled, a desugaring that looks up some constructor K withoutusing it).
It's also a question of whether one would consider these as kludgy. Or whether they sound rather natural to your ears. To me: rather natural, evidently. To you, and most other members of the committee, as far as I could gather, they seem to sound weird and somewhat repulsive.
(Can someone maybe just make GetField work with polytypes? Then we woudn’t have any of this discussion, I guess.)
Cheers to that :-) _______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org
https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.ha skell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering- committee&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005 ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6372436991936 18681&sdata=VoAprdrPQj326%2F7nVXdF0GUk7Y%2BPBAoHM4fBH7w27QE%3D&re served=0 -- Joachim Breitner mail@joachim-breitner.de
https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.joac him- breitner.de%2F&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac514 49005ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C63724369 9193618681&sdata=4a46m7moVg%2BH4dsGr%2F831WHPPQ79cdzfKMDp0wAwoSs%3D&a mp;reserved=0
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.ha skell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fghc-steering- committee&data=02%7C01%7Csimonpj%40microsoft.com%7C1c351455ac51449005 ce08d7f1c4a47b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6372436991936 18681&sdata=VoAprdrPQj326%2F7nVXdF0GUk7Y%2BPBAoHM4fBH7w27QE%3D&re served=0
ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Yes, I'm just saying I'd like to see the actual final proposal we are being asked to approve before approving it.
You pointed to a previous version. But the current version doubtless has been improved in various ways ... let's not lose that. Eg I'd love to see the careful wording about fully settled types appear in the 'builder' alternative section. It's already written .. I'm not suggesting new work.
not a big deal!
Simon
| -----Original Message-----
| From: ghc-steering-committee

On Sat, 2 May 2020 at 10:39, Joachim Breitner
Dear Committee,
it seems discussion has ebbed down. Are there any new arguments that can be brought forward? Did anyone have a change of mind which would bring us closer to (or farther away from) consensus? Or should we vote?
Simon Marlow, Chris, Cale, Tom: You have not stated an opinion. Do you have one?
Ideally the extension would use record overloading, but I understand why that can't work right now (thanks Richard for enlightening me). Given that, I'm not keen on introducing the concept of "fully settled types" - it feels like a kludge, and introducing a special type system concept for this one particular extension doesn't seem like the right tradeoff to me. Perhaps if it was available for general record selection too... but then we have to worry about the interaction with the existing overloaded record selection mechanism. On the other hand, the modules-based approach is simple to describe, conservative, and supports the important uses. It's not ideal, but on balance it seems like the right approach for now. Cheers Simon
Cheers, Joachim
Am Mittwoch, den 22.04.2020, 12:26 +0200 schrieb Joachim Breitner:
Dear Committe,
trying to summarize the discussion here.
The main decision seems to be: Record based or module based.
Record based is preferred by: Arnaud (one of the authors), SPJ
Module based is preferred by: Richard (after changing his mind), Eric, Iavor, Vitaly, Alejandro, me
This is not a vote, merely a summary of sentimenss. But it seems that maybe the onus to make stronger arguments for the record-based approach is on Simon and Arnaud?
Related to the module based variant, there was some discussion about whether the * desugaring should go to a “qualified name”, that is then resolved like any manually written name (i.e. could conceptually be done in the parser) * or if it should go directly a suitable “original name” of a suitable function, even if not explicitly imported (i.e. could conceptually be done in the renamer, but not the parser)
I initially advocated for the latter, arguing with a “you shall not have to import stuff you do not write explicitly” rule. But Simon’s recent argument that
import qualified Monad as M
(i.e. _with_ qualification, but _without_ an import list) will give the developer a good user experience, and if they try to do things like
import Monad as M (runMonad) … M.do …
then, well, they shouldn’t. So I am happy to advocate for the module based approach with the “must be in scope” rule. But maybe we should keep the focus more on the fundamental question above.
We could keep in mind that eventually, people will ask for M.if; M.[ and other “qualified syntax”. But it seems that that will work equally well with either choice here.
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ ghc-steering-committee mailing list ghc-steering-committee@haskell.org https://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-steering-committee
participants (11)
-
Alejandro Serrano Mena
-
Cale Gibbard
-
Eric Seidel
-
Iavor Diatchki
-
Joachim Breitner
-
Richard Eisenberg
-
Simon Marlow
-
Simon Peyton Jones
-
Spiwack, Arnaud
-
Tom Harding
-
Vitaly Bragilevsky