Re: Superclass defaults
> What sayeth ye all? I approve of your suggestion. Well said! > 3. I must admit I don't fully see how client code can be kept from the need > to enable the extension. In particular, if client code must use the explicit > opt-out, this is a rather obvious syntactic change - are you really > suggesting that we enable new syntax without the need to declare what > language (i.e. extensions) the file uses? I can only speak for myself, but I believe the intention was never to allow the syntactic changes without a flag. The only part that I understood to be be flag-less is exactly the extension you call SilentDefaultSuperInstances. Now about the names... I suggest IntrinsicInstanceDeclarations for the auto-enabled extension, and just IntrinsicSuperclasses for the larger extension. Regards, Jonas On 3 September 2011 12:21, Niklas Broberg wrote: > On Mon, Aug 22, 2011 at 10:05 AM, Max Bolingbroke > wrote: >> >> On 21 August 2011 21:03, Alexey Khudyakov >> wrote: >> > I don't completely understant how does it work. Does client need to >> > enable >> > language extension to get default instances? >> >> I think that the extension would only be required to *define them*, >> not for them to be generated. The more conservative choice would >> indeed be to require the extension for both, though. > > Please allow me to voice my not-so-humble opinion that, as a rigid > principle, any extension is ALWAYS enabled with a flag, and never enabled > silently. This is what Max calls the "conservative" choice, while I would > like to call it the "only sensible" choice. > 1. In particular: adhering to this principle makes it much much easier to > achieve consistency between different compilers and tools working with > Haskell code. GHC is currently in so strong a position as to make its own > laws, but with great power comes great responsibility. Even if GHC will > always have the proper context available, it is by no means certain that > other tools will. In this particular instance, it is certainly not > inconceivable or even improbable that tools other than compilers might want > to analyse a module containing instance declarations, without knowing a > priori that some default superclass instances are assumed. Having a flag > always makes this clear. > (As an example, GHC already breaks this principle in (at least) one instance > - MPTC when used as contexts - which creates problems for haskell-src-exts). > > 2. If you are worried about breakage of legacy code, you can always achieve > silent enabling by having a flag and having it on by default. Then GHC can > declare that it, by default, compiles a Haskell extension and not the > standard. This could still create problems when programmers expect other > tools to work on their GHC-specific files, but then at least we have a clear > story and can put blame where blame is due (on GHC). Other tools can then > also opt to enable the same extensions by default, or by flag. Also, it > would make it possible to explicitly remove the flag in client code (with > the appropriate -XNo... flag). > From a principle point of view this is still not a perfect solution, and I > would prefer that an explicit flag was always needed (unless and until the > extension is adopted in the proper Haskell' revision). I realise this would > break a lot of code though, and having a flag enabled by default is far, far > better than not using a flag at all. > 3. I must admit I don't fully see how client code can be kept from the need > to enable the extension. In particular, if client code must use the explicit > opt-out, this is a rather obvious syntactic change - are you really > suggesting that we enable new syntax without the need to declare what > language (i.e. extensions) the file uses? Of course, there is precedent for > such a move with the ugly MPTCs-in-contexts mentioned above - a precedent > that I would very much like to see revoked. I would in any case like to > voice my very strong opinion against such a choice. > > That said - I definitely like the general idea of default superclass > instances, I think the proposed extension is very elegant and fixes a > definite wart. But please let's not introduce other problems by adopting it! > To be concrete, I propose the following named extensions (exact names up for > bike-shed discussion): > * DefaultSuperInstances: Enables the full SHE-bang (pun fully intended), > including in particular the syntactic additions in class and instance > declarations. > * SilentDefaultSuperInstances: Enables ONLY the possibility for client code > to use inherited default instances. This extension is subsumed by > DefaultSuperInstances (except for error/warning behavior discussed below), > just like RankNTypes subsumes Rank2Types. > I further propose that GHC enables SilentDefaultSuperInstances by default, > as a pragmatic choice to avoid legacy issues, but not DefaultSuperInstances. > If (only) SilentDefaultSuperInstances is enabled, I propose that Option 2
Re: Superclass defaults
On Mon, Aug 22, 2011 at 10:05 AM, Max Bolingbroke < batterseapo...@hotmail.com> wrote: > On 21 August 2011 21:03, Alexey Khudyakov > wrote: > > I don't completely understant how does it work. Does client need to > enable > > language extension to get default instances? > > I think that the extension would only be required to *define them*, > not for them to be generated. The more conservative choice would > indeed be to require the extension for both, though. > Please allow me to voice my not-so-humble opinion that, as a rigid principle, any extension is ALWAYS enabled with a flag, and never enabled silently. This is what Max calls the "conservative" choice, while I would like to call it the "only sensible" choice. 1. In particular: adhering to this principle makes it much much easier to achieve consistency between different compilers and tools working with Haskell code. GHC is currently in so strong a position as to make its own laws, but with great power comes great responsibility. Even if GHC will always have the proper context available, it is by no means certain that other tools will. In this particular instance, it is certainly not inconceivable or even improbable that tools other than compilers might want to analyse a module containing instance declarations, without knowing a priori that some default superclass instances are assumed. Having a flag always makes this clear. (As an example, GHC already breaks this principle in (at least) one instance - MPTC when used as contexts - which creates problems for haskell-src-exts). 2. If you are worried about breakage of legacy code, you can always achieve silent enabling by having a flag and having it on by default. Then GHC can declare that it, by default, compiles a Haskell extension and not the standard. This could still create problems when programmers expect other tools to work on their GHC-specific files, but then at least we have a clear story and can put blame where blame is due (on GHC). Other tools can then also opt to enable the same extensions by default, or by flag. Also, it would make it possible to explicitly remove the flag in client code (with the appropriate -XNo... flag). >From a principle point of view this is still not a perfect solution, and I would prefer that an explicit flag was always needed (unless and until the extension is adopted in the proper Haskell' revision). I realise this would break a lot of code though, and having a flag enabled by default is far, far better than not using a flag at all. 3. I must admit I don't fully see how client code can be kept from the need to enable the extension. In particular, if client code must use the explicit opt-out, this is a rather obvious syntactic change - are you really suggesting that we enable new syntax without the need to declare what language (i.e. extensions) the file uses? Of course, there is precedent for such a move with the ugly MPTCs-in-contexts mentioned above - a precedent that I would very much like to see revoked. I would in any case like to voice my very strong opinion against such a choice. That said - I definitely like the general idea of default superclass instances, I think the proposed extension is very elegant and fixes a definite wart. But please let's not introduce other problems by adopting it! To be concrete, I propose the following named extensions (exact names up for bike-shed discussion): * DefaultSuperInstances: Enables the full SHE-bang (pun fully intended), including in particular the syntactic additions in class and instance declarations. * SilentDefaultSuperInstances: Enables ONLY the possibility for client code to use inherited default instances. This extension is subsumed by DefaultSuperInstances (except for error/warning behavior discussed below), just like RankNTypes subsumes Rank2Types. I further propose that GHC enables SilentDefaultSuperInstances by default, as a pragmatic choice to avoid legacy issues, but not DefaultSuperInstances. If (only) SilentDefaultSuperInstances is enabled, I propose that Option 2 is used. It makes perfect sense to warn if you override a default instance, just like it is sensible to warn about name shadowing. Option 3 strikes me as strictly worse. However, if DefaultSuperInstances is enabled (which must then be done explicitly), I propose that Option 1 is used instead. 2011/9/2 Conor McBride > On 2 Sep 2011, at 18:19, Jonas Almström Duregård wrote: > The recent discussion concerns whether option 2 should eventually be > shifted to option 1. Everyone seems to agree that option 2 should be > used initially. > With my proposal this discussion becomes moot. SilentDefaultSuperInstances effects option 2, DefaultSuperInstances effects option 1. The discussion can then instead turn to whether or not DefaultSuperInstances should be adopted in Haskell', and (only) at that point there will be an automatic switch to option 1. A similar warning should perhaps indicate that a "hiding" clause has > nothing to hid
Re: Superclass defaults
I agree. Option 2 FTW :) The recent discussion concerns whether option 2 should eventually be shifted to option 1. Everyone seems to agree that option 2 should be used initially. Regards, Jonas On 2 September 2011 18:55, Simon Peyton-Jones wrote: > Too many words! I'm losing track. What I'm proposing is Option 2 under "The > design of the opt-out mechanism" on > http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances > > I believe that meets everyone's goals: > * A warning encourages you to fix the client code > * But you can turn it off, and it's not fatal. > > Does anyone advocate something else? > > Simon > > | -Original Message- > | From: glasgow-haskell-users-boun...@haskell.org > [mailto:glasgow-haskell-users- > | boun...@haskell.org] On Behalf Of Jonas Almström Duregård > | Sent: 02 September 2011 16:50 > | To: Conor McBride > | Cc: GHC users > | Subject: Re: Superclass defaults > | > | > The question then comes down to whether that warning should ever be > | > strengthened to an error. > | > | Indeed. > | > | > I agree that such a scenario is possible. The present situation gives > | > no choice but to do things badly, but things often get done badly the > | > first time around anyway. Perhaps I'm just grumpy, but I think we > | > should aim to make bad practice erroneous where practicable. Once > | > the mistake is no longer forced upon us, it becomes a mistake that > | > deserves its penalty in labour. Silent pre-emption is bad practice and > | > code which relies on it should be fixed: it's not good to misconstrue > | > an instance declaration because you don't know which instance > | > declarations are somewhere else. Nonmonotonic reasoning is always a > | > bit scary. > | > > | > From a library design perspective, we should certainly try to get these > | > hierarchical choices right when we add classes. I accept that it should > | > be cheap to fix mistakes (especially when the mistake is lack of > | > foresight. Sticking with the warning rather than the error reduces the > | > price of this particular legacy fix at the cost of tolerating misleading > | > code. I agree that the balance of this trade-off is with the warning, > | > for the moment, but I expect it to shift over time towards the error. > | > But if it's clear what the issue is, then we can at least keep it under > | > review. > | > | I agree. Making bad practice erroneous is good, but its not really the > | bad practice that raises the error here. You have no serious problems > | until you try to change your bad design to a good one. Like you say it > | should be cheap to fix mistakes. > | > | >> Will there be a solution to this dilemma that I have missed? Should > | >> the client code be allowed opt-out from the superclass preemptively > | >> before it is given a default? Won't that cause a similar perplexity? > | > > | > I don't know what you mean by this. Perhaps you could expand on it? > | > | What I'm trying to ask is if you can write compatible code that will > | work across gradual changes of the compiler and the libraries. > | > | Suppose we have library with class C. In a newer version of the > | library we add an intrinsic superclass S. Also suppose the compiler > | implements option 1. Now the users of the library want to write code > | that uses both C and S, and that's compatible with both the new and > | the old library. From what I can tell there are three situations that > | needs to be covered: > | > | 1) Old compiler - Old library > | Here we need to specify both instances, and we cant hide the default S > | instance because its not supported by the compiler. This also applies > | for other situations where the client must use Haskell 2010 compatible > | code. > | > | 2) New compiler - Old library > | Here we also need to specify both instances. > | > | 3) New compiler - New library > | We can either write both instances and hide the default or we can just > | write an instance for C. > | > | Clearly code that covers situation 1 will never be compatible with > situation 3. > | > | The question I was asking was if we are allowed to hide the default > | instance of S in situation 2. In that case you can write compatible > | code for situation 2 and 3. The possible confusion from this is that > | you hide a default implementation thats not defined. Maybe it's not as > | bad as overriding silently, but there is some room for error where you > | think you have blocked a superclass instance but really you have just > | blocked some completely unrelated cl
RE: Superclass defaults
Too many words! I'm losing track. What I'm proposing is Option 2 under "The design of the opt-out mechanism" on http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances I believe that meets everyone's goals: * A warning encourages you to fix the client code * But you can turn it off, and it's not fatal. Does anyone advocate something else? Simon | -Original Message- | From: glasgow-haskell-users-boun...@haskell.org [mailto:glasgow-haskell-users- | boun...@haskell.org] On Behalf Of Jonas Almström Duregård | Sent: 02 September 2011 16:50 | To: Conor McBride | Cc: GHC users | Subject: Re: Superclass defaults | | > The question then comes down to whether that warning should ever be | > strengthened to an error. | | Indeed. | | > I agree that such a scenario is possible. The present situation gives | > no choice but to do things badly, but things often get done badly the | > first time around anyway. Perhaps I'm just grumpy, but I think we | > should aim to make bad practice erroneous where practicable. Once | > the mistake is no longer forced upon us, it becomes a mistake that | > deserves its penalty in labour. Silent pre-emption is bad practice and | > code which relies on it should be fixed: it's not good to misconstrue | > an instance declaration because you don't know which instance | > declarations are somewhere else. Nonmonotonic reasoning is always a | > bit scary. | > | > From a library design perspective, we should certainly try to get these | > hierarchical choices right when we add classes. I accept that it should | > be cheap to fix mistakes (especially when the mistake is lack of | > foresight. Sticking with the warning rather than the error reduces the | > price of this particular legacy fix at the cost of tolerating misleading | > code. I agree that the balance of this trade-off is with the warning, | > for the moment, but I expect it to shift over time towards the error. | > But if it's clear what the issue is, then we can at least keep it under | > review. | | I agree. Making bad practice erroneous is good, but its not really the | bad practice that raises the error here. You have no serious problems | until you try to change your bad design to a good one. Like you say it | should be cheap to fix mistakes. | | >> Will there be a solution to this dilemma that I have missed? Should | >> the client code be allowed opt-out from the superclass preemptively | >> before it is given a default? Won't that cause a similar perplexity? | > | > I don't know what you mean by this. Perhaps you could expand on it? | | What I'm trying to ask is if you can write compatible code that will | work across gradual changes of the compiler and the libraries. | | Suppose we have library with class C. In a newer version of the | library we add an intrinsic superclass S. Also suppose the compiler | implements option 1. Now the users of the library want to write code | that uses both C and S, and that's compatible with both the new and | the old library. From what I can tell there are three situations that | needs to be covered: | | 1) Old compiler - Old library | Here we need to specify both instances, and we cant hide the default S | instance because its not supported by the compiler. This also applies | for other situations where the client must use Haskell 2010 compatible | code. | | 2) New compiler - Old library | Here we also need to specify both instances. | | 3) New compiler - New library | We can either write both instances and hide the default or we can just | write an instance for C. | | Clearly code that covers situation 1 will never be compatible with situation 3. | | The question I was asking was if we are allowed to hide the default | instance of S in situation 2. In that case you can write compatible | code for situation 2 and 3. The possible confusion from this is that | you hide a default implementation thats not defined. Maybe it's not as | bad as overriding silently, but there is some room for error where you | think you have blocked a superclass instance but really you have just | blocked some completely unrelated class. | | Of course we can get compatibility across all three using CPP but I | really wish we won't need that. | | As time passes, situation 1 will become more rare, although situation | 2 and 3 can reoccur endlessly as new libraries are designed and | redesigned. | | Regards, | Jonas | | ___ | Glasgow-haskell-users mailing list | Glasgow-haskell-users@haskell.org | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On 2 Sep 2011, at 18:19, Jonas Almström Duregård wrote: I agree. Option 2 FTW :) The recent discussion concerns whether option 2 should eventually be shifted to option 1. Everyone seems to agree that option 2 should be used initially. A similar warning should perhaps indicate that a "hiding" clause has nothing to hide, as Jonas suggests. I'm in favour of Option 2 now and Option 1 later, where "later" has non-disruptiveness criteria attached. A bit like being in favour of the pound now and the euro later. As I've mentioned in another message just now, even Option 2 entails some disruption, when you make one old class a new superclass-with- default of another (e.g. Functor for Monad): if old code makes M a Functor in a later module than its Monad instance, that Functor M instance comes too late to pre-empt the default and is rejected as a duplicate. All the best Conor Regards, Jonas On 2 September 2011 18:55, Simon Peyton-Jones wrote: Too many words! I'm losing track. What I'm proposing is Option 2 under "The design of the opt-out mechanism" on http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances I believe that meets everyone's goals: * A warning encourages you to fix the client code * But you can turn it off, and it's not fatal. Does anyone advocate something else? Simon | -Original Message- | From: glasgow-haskell-users-boun...@haskell.org [mailto:glasgow-haskell-users- | boun...@haskell.org] On Behalf Of Jonas Almström Duregård | Sent: 02 September 2011 16:50 | To: Conor McBride | Cc: GHC users | Subject: Re: Superclass defaults | | > The question then comes down to whether that warning should ever be | > strengthened to an error. | | Indeed. | | > I agree that such a scenario is possible. The present situation gives | > no choice but to do things badly, but things often get done badly the | > first time around anyway. Perhaps I'm just grumpy, but I think we | > should aim to make bad practice erroneous where practicable. Once | > the mistake is no longer forced upon us, it becomes a mistake that | > deserves its penalty in labour. Silent pre-emption is bad practice and | > code which relies on it should be fixed: it's not good to misconstrue | > an instance declaration because you don't know which instance | > declarations are somewhere else. Nonmonotonic reasoning is always a | > bit scary. | > | > From a library design perspective, we should certainly try to get these | > hierarchical choices right when we add classes. I accept that it should | > be cheap to fix mistakes (especially when the mistake is lack of | > foresight. Sticking with the warning rather than the error reduces the | > price of this particular legacy fix at the cost of tolerating misleading | > code. I agree that the balance of this trade-off is with the warning, | > for the moment, but I expect it to shift over time towards the error. | > But if it's clear what the issue is, then we can at least keep it under | > review. | | I agree. Making bad practice erroneous is good, but its not really the | bad practice that raises the error here. You have no serious problems | until you try to change your bad design to a good one. Like you say it | should be cheap to fix mistakes. | | >> Will there be a solution to this dilemma that I have missed? Should | >> the client code be allowed opt-out from the superclass preemptively | >> before it is given a default? Won't that cause a similar perplexity? | > | > I don't know what you mean by this. Perhaps you could expand on it? | | What I'm trying to ask is if you can write compatible code that will | work across gradual changes of the compiler and the libraries. | | Suppose we have library with class C. In a newer version of the | library we add an intrinsic superclass S. Also suppose the compiler | implements option 1. Now the users of the library want to write code | that uses both C and S, and that's compatible with both the new and | the old library. From what I can tell there are three situations that | needs to be covered: | | 1) Old compiler - Old library | Here we need to specify both instances, and we cant hide the default S | instance because its not supported by the compiler. This also applies | for other situations where the client must use Haskell 2010 compatible | code. | | 2) New compiler - Old library | Here we also need to specify both instances. | | 3) New compiler - New library | We can either write both instances and hide the default or we can just | write an instance for C. | | Clearly code that covers situation 1 will never be compatible with situation 3. | | The question I was asking was if we are allowed to hide the default | instance of S in situation 2. In t
Re: Superclass defaults
Hi On 2 Sep 2011, at 16:34, Brandon Allbery wrote: I hope I am misunderstanding this I wrote: I agree that such a scenario is possible. The present situation gives no choice but to do things badly, but things often get done badly the first time around anyway. Perhaps I'm just grumpy, but I think we should aim to make bad practice erroneous where practicable. Once the mistake is no longer forced upon us, it becomes a mistake that deserves its penalty in labour. Silent pre-emption is bad practice and with the response: So, when the whole point is that an unfortunate design years ago can't be reasonably fixed without rewriting massive amounts of code, the only correct answer is to rewrite massive amounts of code? I'm not sure what you're asking here. Of course we should compare the pain of the treatment with that of the symptoms. Especially when the original proposal was put forward *specifically to avoid* rewriting massive amounts of code? Which original proposal? How does it avoid rewriting code? Yes, we'd love a perfect world. We don't have one. That's the *point*. Recall that Option 2 resolves the duplicate superclass instances in favour of an explicit prior instance, but issues a warning (which should offer the data to choose between explicit resolutions). That deals with a chunk of the legacy scenario (although it doesn't handle the situation where some M is made a Monad in one module and made Applicative in a later module, which is possible (common, even?) because Applicative is not currently a superclass of Monad). If we make one existing class a superclass of another existing class, some disruption is inevitable: we can try to minimize that disruption, but we can't eliminate it entirely. For another example, if some F is made Applicative and Traversable in the same module, which default Functor instance pre-empts the other? We should question whether the disruption of even Option 2/3 makes it worth adding default superclass instances at all. Maybe, depressingly, we've reached the can't-fix-it stage. It would be good to get some data. It's also worth considering tools to support migration, using the diagnostics generated by warnings. If it is worth adding default superclass instances, Option 2 looks like a crucial disruption-minimizing expedience, while we have a legacy of newly extraneous instances to deal with. As far as "an unfortunate design years ago" is concerned, we should be careful to minimize the amount of rewriting required. If that minimum is still too much, we'd better not go there. I'm in favour of moving to Option 1 eventually, as somehow the better choice for code comprehension. But I can see reasons to resist the changeover: * too much unmigrated code still relying on pre-emption under Option 2; * new instances of the old problem (an existing S is suddenly made a superclass of an existing C, with a default S instance for C) come into being, The former is a real risk, but hopefully with a finite lifespan. It would be too costly to switch from a warning to an error while too much code relies on the deprecated practice. Please don't imagine I'm in favour of that. The latter, however, requires us to be a bit dim in a way which was certainly not in evidence when most of the current motivating examples arose. In the legacy code (Monad-Applicative-Functor, Traversable-Foldable-Functor), we've had to choose between two bad options, but the candidate superclass-with-default-implementation has usually been evident. I'm sure we're capable of being that dim. I'm also sure we're capable of screwing up by writing an instance and assuming we get the default superclass instance we expect, without noticing that someone else's chunk of the codebase pre-empts it. I'd be troubled if someone knackered my applicative- style use of Monad [] by adding a zipping Applicative [], or even an instance which appeared to have the same functionality but also did some sneaky unsafePerformIO badness. That's an example of the risk we take by allowing pre-emption. We have to balance the risk of going back and resolving duplicates with the risk of bugs caused by code meaning less than it says. So, are default superclass instances just too disruptive? All the best Conor PS We'd love a perfect world. We don't have one. That's why we change things. ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
> The question then comes down to whether that warning should ever be > strengthened to an error. Indeed. > I agree that such a scenario is possible. The present situation gives > no choice but to do things badly, but things often get done badly the > first time around anyway. Perhaps I'm just grumpy, but I think we > should aim to make bad practice erroneous where practicable. Once > the mistake is no longer forced upon us, it becomes a mistake that > deserves its penalty in labour. Silent pre-emption is bad practice and > code which relies on it should be fixed: it's not good to misconstrue > an instance declaration because you don't know which instance > declarations are somewhere else. Nonmonotonic reasoning is always a > bit scary. > > From a library design perspective, we should certainly try to get these > hierarchical choices right when we add classes. I accept that it should > be cheap to fix mistakes (especially when the mistake is lack of > foresight. Sticking with the warning rather than the error reduces the > price of this particular legacy fix at the cost of tolerating misleading > code. I agree that the balance of this trade-off is with the warning, > for the moment, but I expect it to shift over time towards the error. > But if it's clear what the issue is, then we can at least keep it under > review. I agree. Making bad practice erroneous is good, but its not really the bad practice that raises the error here. You have no serious problems until you try to change your bad design to a good one. Like you say it should be cheap to fix mistakes. >> Will there be a solution to this dilemma that I have missed? Should >> the client code be allowed opt-out from the superclass preemptively >> before it is given a default? Won't that cause a similar perplexity? > > I don't know what you mean by this. Perhaps you could expand on it? What I'm trying to ask is if you can write compatible code that will work across gradual changes of the compiler and the libraries. Suppose we have library with class C. In a newer version of the library we add an intrinsic superclass S. Also suppose the compiler implements option 1. Now the users of the library want to write code that uses both C and S, and that's compatible with both the new and the old library. From what I can tell there are three situations that needs to be covered: 1) Old compiler - Old library Here we need to specify both instances, and we cant hide the default S instance because its not supported by the compiler. This also applies for other situations where the client must use Haskell 2010 compatible code. 2) New compiler - Old library Here we also need to specify both instances. 3) New compiler - New library We can either write both instances and hide the default or we can just write an instance for C. Clearly code that covers situation 1 will never be compatible with situation 3. The question I was asking was if we are allowed to hide the default instance of S in situation 2. In that case you can write compatible code for situation 2 and 3. The possible confusion from this is that you hide a default implementation thats not defined. Maybe it's not as bad as overriding silently, but there is some room for error where you think you have blocked a superclass instance but really you have just blocked some completely unrelated class. Of course we can get compatibility across all three using CPP but I really wish we won't need that. As time passes, situation 1 will become more rare, although situation 2 and 3 can reoccur endlessly as new libraries are designed and redesigned. Regards, Jonas ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
I hope I am misunderstanding this 2011/9/2 Conor McBride > Also, if I understand you correctly, you say the current situation is >> exceptional, and suggest option 2 as a temporary solution to it. You >> seem convinced that these kind of situations will not appear in the >> future, but I'm not as optimistic about that. >> >> Even when superclass defaults are implemented, people will >> occasionally implement classes without realizing that there is a >> suitable intrinsic superclass (or add the superclass but not the >> default instance). People will start using the new class and give >> separate instances for the superclass, and eventually someone will >> point out that the there should be a default instance for the >> superclass. Now if option 1 is implemented, the library maintainers >> will be reluctant to add the superclass instance because it will break >> a lot of client code. >> > > I agree that such a scenario is possible. The present situation gives > no choice but to do things badly, but things often get done badly the > first time around anyway. Perhaps I'm just grumpy, but I think we > should aim to make bad practice erroneous where practicable. Once > the mistake is no longer forced upon us, it becomes a mistake that > deserves its penalty in labour. Silent pre-emption is bad practice and > So, when the whole point is that an unfortunate design years ago can't be reasonably fixed without rewriting massive amounts of code, the only correct answer is to rewrite massive amounts of code? Especially when the original proposal was put forward *specifically to avoid* rewriting massive amounts of code? Yes, we'd love a perfect world. We don't have one. That's the *point*. -- brandon s allbery allber...@gmail.com wandering unix systems administrator (available) (412) 475-9364 vm/sms ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
Hi On 2 Sep 2011, at 10:55, Jonas Almström Duregård wrote: On 31 August 2011 12:22, Conor McBride wrote: I become perplexed very easily. I think we should warn whenever silent pre-emption (rather than explicit) hiding is used to suppress a default instance, because it is bad --- it makes the meaning of an instance declaration rather more context dependent. Perhaps a design principle should be that to understand an instance declaration, you need only know (in addition) the tower of class declaration s above it: it is subtle and worrying to make the meaning of one instance declaration depend on the presence or absence of others. Those are all good arguments, and you've convinced me that always warning is better. The question then comes down to whether that warning should ever be strengthened to an error. First of all, I think the design goal is quite clear: "a class C can be re-factored into a class C with a superclass S, without disturbing any clients". Requiring client C to opt-out from the default implementation of S is a clear violation of the design goal. So I disagree that option 1 can be compatible with the design goal, but like you say the design goal might be at fault. Design goal 1 does not explicitly distinguish the scenarios where S is pre-existing or being introduced afresh. If the former, it's inaccurate to describe what's happening as refactoring C, for S is experiencing some fall-out, too. We should clearly seek more precision, one way or another. Also, if I understand you correctly, you say the current situation is exceptional, and suggest option 2 as a temporary solution to it. You seem convinced that these kind of situations will not appear in the future, but I'm not as optimistic about that. Even when superclass defaults are implemented, people will occasionally implement classes without realizing that there is a suitable intrinsic superclass (or add the superclass but not the default instance). People will start using the new class and give separate instances for the superclass, and eventually someone will point out that the there should be a default instance for the superclass. Now if option 1 is implemented, the library maintainers will be reluctant to add the superclass instance because it will break a lot of client code. I agree that such a scenario is possible. The present situation gives no choice but to do things badly, but things often get done badly the first time around anyway. Perhaps I'm just grumpy, but I think we should aim to make bad practice erroneous where practicable. Once the mistake is no longer forced upon us, it becomes a mistake that deserves its penalty in labour. Silent pre-emption is bad practice and code which relies on it should be fixed: it's not good to misconstrue an instance declaration because you don't know which instance declarations are somewhere else. Nonmonotonic reasoning is always a bit scary. From a library design perspective, we should certainly try to get these hierarchical choices right when we add classes. I accept that it should be cheap to fix mistakes (especially when the mistake is lack of foresight. Sticking with the warning rather than the error reduces the price of this particular legacy fix at the cost of tolerating misleading code. I agree that the balance of this trade-off is with the warning, for the moment, but I expect it to shift over time towards the error. But if it's clear what the issue is, then we can at least keep it under review. Will there be a solution to this dilemma that I have missed? Should the client code be allowed opt-out from the superclass preemptively before it is given a default? Won't that cause a similar perplexity? I don't know what you mean by this. Perhaps you could expand on it? All the best Conor ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
Hi, On 31 August 2011 12:22, Conor McBride wrote: > I become perplexed very easily. I think we should warn whenever silent > pre-emption (rather than explicit) hiding is used to suppress a default > instance, because it is bad --- it makes the meaning of an instance > declaration rather more context dependent. Perhaps a design principle > should be that to understand an instance declaration, you need only > know (in addition) the tower of class declaration s above it: it is > subtle and worrying to make the meaning of one instance declaration > depend on the presence or absence of others. Those are all good arguments, and you've convinced me that always warning is better. > Arguably, option 1 does not conflict with design goal 1. Design goal 1 > supports a methodology for refining a class into a hierarchy without > creating the need for stacks of default instances or breaking code. If > the new superclass is a brand new thing without legacy instances, > there's no problem. If we'd had this mechanism in place, Functor would > always have been made a superclass of Monad, Applicative would have > been easily inserted, and we wouldn't have the stacks of manually added > default instances to worry about. > > The main problem with Option 1 is in dealing with the legacy of > classes which currently require a stack of default instances, creating > a hierarchy from parts which already exist. Option 1 would create a bunch > of instance conflicts and thus demand changes to code. Design goal 1 > isn't very explicit (sorry!) about this distinction between introducing > new classes as superclasses and building hierarchies from legacy classes, > but it was the former I intended. I always expected the latter to cause > trouble. > > If it is also a design goal to minimize damage with respect to the > current unfortunate situation, then Option 1 is problematic. Whatever we > might wish, we are where we are. We should be pragmatic. I think we > should set Option 1 as the direction of travel, but go with Option 2 for > the moment. We should make sure that the warnings generated by Option 2 > are sufficiently informative that it is easy to track down the conflicts > and resolve them explicitly, for Option 1 compliance. First of all, I think the design goal is quite clear: "a class C can be re-factored into a class C with a superclass S, without disturbing any clients". Requiring client C to opt-out from the default implementation of S is a clear violation of the design goal. So I disagree that option 1 can be compatible with the design goal, but like you say the design goal might be at fault. Also, if I understand you correctly, you say the current situation is exceptional, and suggest option 2 as a temporary solution to it. You seem convinced that these kind of situations will not appear in the future, but I'm not as optimistic about that. Even when superclass defaults are implemented, people will occasionally implement classes without realizing that there is a suitable intrinsic superclass (or add the superclass but not the default instance). People will start using the new class and give separate instances for the superclass, and eventually someone will point out that the there should be a default instance for the superclass. Now if option 1 is implemented, the library maintainers will be reluctant to add the superclass instance because it will break a lot of client code. Will there be a solution to this dilemma that I have missed? Should the client code be allowed opt-out from the superclass preemptively before it is given a default? Won't that cause a similar perplexity? Regards, Jonas ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On Wed, Aug 31, 2011 at 4:21 PM, Simon Peyton-Jones wrote: > There seems to be a lot of support for Option 3... but what about Option 2 > (ie pre-empt but give a warning)? I notice that the idea to only issue a warning if the explicit and implicit instances are in different modules was already halfway towards reaching option 2. I think it is fine to issue warnings also if both instances are in the same module. For newly written code, an explicit hiding clause removes context dependence (as Conor points out) so the warning is justified. For existing code where it generates too much noise the warning could be switched of selectively until the code gets cleaned up. Sebastian ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
Hi Sorry to be late again...I'm trying to have what's laughably described as a holiday, but it seems more like the common cold to me. On 31 Aug 2011, at 08:52, Jonas Almström Duregård wrote: | > There seems to be a lot of support for Option 3... but what about Option 2 (ie pre-empt but give a warning)? At least in the short term, I think Option 2 is a good compromise. It's true that when I started using default superclass instances (which SHE supports) in the Epigram codebase, the first thing I had to do was delete a bunch of dull default instance stacks. That was fun, but it wasn't nothing. I think option 2 sounds very good. Possibly with the exception of only warning when the manual instance is in another module, since you will never experience the "perplexity" described in option 3 if you have written the instance yourself. I become perplexed very easily. I think we should warn whenever silent pre-emption (rather than explicit) hiding is used to suppress a default instance, because it is bad --- it makes the meaning of an instance declaration rather more context dependent. Perhaps a design principle should be that to understand an instance declaration, you need only know (in addition) the tower of class declaration s above it: it is subtle and worrying to make the meaning of one instance declaration depend on the presence or absence of others. Arguably, option 1 does not conflict with design goal 1. Design goal 1 supports a methodology for refining a class into a hierarchy without creating the need for stacks of default instances or breaking code. If the new superclass is a brand new thing without legacy instances, there's no problem. If we'd had this mechanism in place, Functor would always have been made a superclass of Monad, Applicative would have been easily inserted, and we wouldn't have the stacks of manually added default instances to worry about. The main problem with Option 1 is in dealing with the legacy of classes which currently require a stack of default instances, creating a hierarchy from parts which already exist. Option 1 would create a bunch of instance conflicts and thus demand changes to code. Design goal 1 isn't very explicit (sorry!) about this distinction between introducing new classes as superclasses and building hierarchies from legacy classes, but it was the former I intended. I always expected the latter to cause trouble. If it is also a design goal to minimize damage with respect to the current unfortunate situation, then Option 1 is problematic. Whatever we might wish, we are where we are. We should be pragmatic. I think we should set Option 1 as the direction of travel, but go with Option 2 for the moment. We should make sure that the warnings generated by Option 2 are sufficiently informative that it is easy to track down the conflicts and resolve them explicitly, for Option 1 compliance. Does this sound plausible? Conor ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
| > There seems to be a lot of support for Option 3... but what about Option 2 (ie pre-empt but give a warning)? I think option 2 sounds very good. Possibly with the exception of only warning when the manual instance is in another module, since you will never experience the "perplexity" described in option 3 if you have written the instance yourself. This means that most modules will not get any warning when a class is changed to give a default superclass instance. Even omitting the warning only when the instantiated datatype is in the same module as the instances might be enough to suppress most warnings. Also i wonder if there will be a way of suppressing the warnings and still have compatible code (with the current compiler, preferably without using CPP)? /J On 31 August 2011 09:21, Simon Peyton-Jones wrote: > | > Won't option 1 "Reject this as a duplicate instance declaration, which > | > indeed it is." conflict with design goal 1: "a class C can be > | > re-factored into a class C with a superclass S, without disturbing any > | > clients"? > > Yes, option 1 does conflict with design goal 1; good point. There seems to > be a lot of support for Option 3... but what about Option 2 (ie pre-empt but > give a warning)? > > I've updated the wiki page > http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances to > reflect this discussion. > > Simon > > > | -Original Message- > | From: glasgow-haskell-users-boun...@haskell.org > [mailto:glasgow-haskell-users- > | boun...@haskell.org] On Behalf Of Sebastian Fischer > | Sent: 30 August 2011 03:49 > | To: Bas van Dijk > | Cc: glasgow-haskell-users@haskell.org; Simon Peyton-Jones > | Subject: Re: Superclass defaults > | > | On Mon, Aug 29, 2011 at 6:21 AM, Bas van Dijk wrote: > | > | > Won't option 1 "Reject this as a duplicate instance declaration, which > | > indeed it is." conflict with design goal 1: "a class C can be > | > re-factored into a class C with a superclass S, without disturbing any > | > clients"? > | > | If yes, I prefer option 3: > | > | > "Allow the explicit to supersede the intrinsic default silently". > | > | The argument against this option is: > | > | > I might notice > | > that Foo is a monad and add a Monad Foo instance in my own code, > | > expecting the Applicative Foo instance to be generated in concert; to > | > my horror, I find my code has subtle bugs because the package > | > introduced a different, non-monadic, Applicative Foo instance which > | > I'm accidentally using instead. > | > | This seems rare enough that it's feasible to issue a warning if a > | default instance is overwritten by an explicit instance in a different > | module which would prevent the described perplexity. This wouldn't, > | for example, disturb the transformers example mentioned by Bas because > | (I think) all instances are defined in the same module. > | > | Sebastian > | > | ___ > | Glasgow-haskell-users mailing list > | Glasgow-haskell-users@haskell.org > | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users > > > ___ > Glasgow-haskell-users mailing list > Glasgow-haskell-users@haskell.org > http://www.haskell.org/mailman/listinfo/glasgow-haskell-users > ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
RE: Superclass defaults
| > Won't option 1 "Reject this as a duplicate instance declaration, which | > indeed it is." conflict with design goal 1: "a class C can be | > re-factored into a class C with a superclass S, without disturbing any | > clients"? Yes, option 1 does conflict with design goal 1; good point. There seems to be a lot of support for Option 3... but what about Option 2 (ie pre-empt but give a warning)? I've updated the wiki page http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances to reflect this discussion. Simon | -Original Message- | From: glasgow-haskell-users-boun...@haskell.org [mailto:glasgow-haskell-users- | boun...@haskell.org] On Behalf Of Sebastian Fischer | Sent: 30 August 2011 03:49 | To: Bas van Dijk | Cc: glasgow-haskell-users@haskell.org; Simon Peyton-Jones | Subject: Re: Superclass defaults | | On Mon, Aug 29, 2011 at 6:21 AM, Bas van Dijk wrote: | | > Won't option 1 "Reject this as a duplicate instance declaration, which | > indeed it is." conflict with design goal 1: "a class C can be | > re-factored into a class C with a superclass S, without disturbing any | > clients"? | | If yes, I prefer option 3: | | > "Allow the explicit to supersede the intrinsic default silently". | | The argument against this option is: | | > I might notice | > that Foo is a monad and add a Monad Foo instance in my own code, | > expecting the Applicative Foo instance to be generated in concert; to | > my horror, I find my code has subtle bugs because the package | > introduced a different, non-monadic, Applicative Foo instance which | > I'm accidentally using instead. | | This seems rare enough that it's feasible to issue a warning if a | default instance is overwritten by an explicit instance in a different | module which would prevent the described perplexity. This wouldn't, | for example, disturb the transformers example mentioned by Bas because | (I think) all instances are defined in the same module. | | Sebastian | | ___ | Glasgow-haskell-users mailing list | Glasgow-haskell-users@haskell.org | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
I was thinking about the design of superclass default instances. I think that we can get relatively far using the following extensions together: 1) Multiple instance declarations instance (Functor[a], Monad [a]) where fmap = map (>>=) = flip concatMap return = (:[]) -- Declaration above is syntactic sugar for 2 declarations: -- instance Functor[a] -- where --fmap = map -- instance Monad [a] -- where --(>>=) = flip concatMap --return = (:[]) 2) Context synonyms -- (MonadAndFunctor a) is synonym for (Functor a, Monad a) context (Functor a, Monad a) => MonadAndFunctor a -- Using synonims with multiple class declarations we can define instances like instance MonadAndFunctor [a] where fmap = map (>>=) = flip concatMap return = (:[]) -- Declaration above is syntactic sugar for -- instance (Functor[a], Monad [a]) -- where --fmap = map --(>>=) = flip concatMap --return = (:[]) 3) And finally Default superclass instances Class contains default instances for superclasses: class Functor m => Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a -- default superclass instance: instance Functor m where fmap f m = m >>= (return . f) Default superclass implementations are used only when multiple instance declarations are used: -- no default superclass instance is used. Error is emitted when there is no Functor instance instance Monad [a] where ... -- default superclass instance is used: instance Functor [a], Monad [a] where (>>=) = ... return = ... -- functor instance is generated automatically -- fmap = ... Suppose that we make Functor to be Monad's superclass. Combination of this three extensions allows us to define compatibility modules: module Control.Monad.Compat (Monad) where import qualified Control.Monad (Monad(..), Functor(..)) as CM context CM.Functor m, CM.Monad m => Monad m When we have compilation failure in client code after our Monad definition change: "No Functor instance found for Foo": instance Monad Foo where ... , we simply add following two lines to the module: import Prelude hiding (Monad) import Control.Monad.Compat (Monad) and compilation succeeds. Pros: Client code can remain Haskell 98/2010 and doesn't require any extensions. Three extensions seems simple when separate (I think there are many corner cases) Cons: Intervention is required into client code (But I think it is required anyway). -- Victor Nazarov ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On Mon, Aug 29, 2011 at 6:21 AM, Bas van Dijk wrote: > Won't option 1 "Reject this as a duplicate instance declaration, which > indeed it is." conflict with design goal 1: "a class C can be > re-factored into a class C with a superclass S, without disturbing any > clients"? If yes, I prefer option 3: > "Allow the explicit to supersede the intrinsic default silently". The argument against this option is: > I might notice > that Foo is a monad and add a Monad Foo instance in my own code, > expecting the Applicative Foo instance to be generated in concert; to > my horror, I find my code has subtle bugs because the package > introduced a different, non-monadic, Applicative Foo instance which > I'm accidentally using instead. This seems rare enough that it's feasible to issue a warning if a default instance is overwritten by an explicit instance in a different module which would prevent the described perplexity. This wouldn't, for example, disturb the transformers example mentioned by Bas because (I think) all instances are defined in the same module. Sebastian ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
Hi, First of all, I love the idea of default superclass instances! About the opt-out feature, the problem with option 3 is only present if the superclass instance is defined in another module (you won't see unexpected behavior from your own instances). One solution is to use option 3 if the offending is defined in the same module and reject/warn only when there is a superclass instance in another module. This way users are not surprised when the compiler rejects a Functor/Applicative pair because they need to explicitly tell it to hide the intrinsic Functor, and the case that Functor and Applicative instances are defined in separate modules must be relatively rare (outside the Applicative library)? We might even require the data type to be defined in the same module (which i guess is easier to check and still very common) to silently override a default? Documenting this feature might not be very pretty i guess... I think the most important thing is to enable users to write compatible code, i.e. modules that works with the Applicative class whether it defines a default Functor or not. I suppose if option 1 is used, hiding should be allowed even for classes that would not have been defined anyway (with a warning)? If we allow the Multi-headed instance declarations described in the suggestion then we can always write compatible code by merging instances, since an instance for (Functor,Applicative) would always hide the default Functor instance if there is one (right?). Regards, Jonas On 28 August 2011 23:21, Bas van Dijk wrote: > On 22 August 2011 10:10, Simon Peyton-Jones wrote: >> | > I don't completely understant how does it work. Does client need to >> enable >> | > language extension to get default instances? >> | >> | I think that the extension would only be required to *define them*, >> | not for them to be generated. The more conservative choice would >> | indeed be to require the extension for both, though. >> >> Yes. I've clarified >> http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances to say >> this. >> >> | > Also proposal cannot fix Functor/Applicative/Monad problem without >> breaking >> | > client code. It requires explicit opt-out but client may define >> Applicative >> | > instance. And unless "hiding" is added it will result in compile error. >> | >> | I think the intention (at least as I understand it) is that a >> | superclass default is only used to generate an instance if there is >> | not already some suitable instance in scope, just like a default >> | method is only used if there is not some explicit definition for the >> | method in the instance. >> >> Actually that is not what Conor and I proposed. See >> http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances. Under >> "Variations" we discuss the "silent-opt-out" choice. But it's bad enough >> knowing exactly what instances are in scope (given that they are not named), >> let alone having that control what further instances are or are not >> generated! For superclass defaults there is no such ambiguity. >> >> Simon >> >> >> ___ >> Glasgow-haskell-users mailing list >> Glasgow-haskell-users@haskell.org >> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users >> > > Won't option 1 "Reject this as a duplicate instance declaration, which > indeed it is." conflict with design goal 1: "a class C can be > re-factored into a class C with a superclass S, without disturbing any > clients"? > > Take the transformers package for example. It defines lot's of > instances of Functor and Applicative for its monad transformers. > Doesn't transformers have to be changed if we go for option 1 (by > either dropping the Functor and Applicative instances or listing > hiding clauses in the Monad instances) thereby seriously conflicting > with the design goal of not disturbing any clients. > > I expected the semantics to be like option 3: "Allow the explicit to > supersede the intrinsic default silently". It has the advantage of: > > 1) Not disturbing any client code. > > 2) Giving the ability to define optimized implementations if you're > not happy with the default ones (without using the hiding mechanism). > > 3) Feeling very much like the semantics of default methods thereby > conforming to the rule of least surprise. > > The argument against option 3, which I quote: > > "Option 3 avoids that problem but risks perplexity: if I make use of > some cool package which introduces some Foo :: * -> *, I might notice > that Foo is a monad and add a Monad Foo instance in my own code, > expecting the Applicative Foo instance to be generated in concert; to > my horror, I find my code has subtle bugs because the package > introduced a different, non-monadic, Applicative Foo instance which > I'm accidentally using instead." > > talks about "subtle bugs". Could you give an example of such a bug? > > I would expect that the non-monadic Applicative Foo instance i
Re: Superclass defaults
On 29 August 2011 11:11, Aleksey Khudyakov wrote: >> "Option 3 avoids that problem but risks perplexity: if I make use of >> some cool package which introduces some Foo :: * -> *, I might notice >> that Foo is a monad and add a Monad Foo instance in my own code, >> expecting the Applicative Foo instance to be generated in concert; to >> my horror, I find my code has subtle bugs because the package >> introduced a different, non-monadic, Applicative Foo instance which >> I'm accidentally using instead." >> >> talks about "subtle bugs". Could you give an example of such a bug? >> >> I would expect that the non-monadic Applicative Foo instance is always >> somehow "compatible" with the monadic one. However I don't have a >> clear definition of "compatible" yet... >> > I think it's something like that. Module Foo defines list and make > ZipList-like Applicative instance. Would you add standard list monad > you have a bug. > > But if you add monad instance which is not compatible with existing > applicative you have bug whether you use extension or not. > > module Foo where > data [a] = a : [a] | [] > > instance Functor [] where > fmap = map > instamce Applicative [] where > pure = repeat > (<*>) = zipWith ($) > > module Main where > instance Monad [] where > return x = [x] > (>>=) = concatMap > Indeed. So in other words your saying that if a programmer uses a module which defines a stream-like list type like for example: newtype StreamList a = SL { toList :: [a] } instance Functor StreamList where fmap f (SL xs) = SL (map f xs) instance Applicative StreamList where pure x = SL $ repeat x SL fs <*> SL xs = SL $ zipWith ($) fs xs And she decides to add a monad instance like the regular list monad: instance Monad StreamList where return x = SL [x] xs >>= f = SL $ concatMap (toList . f) $ toList xs That would be a mistake on her part since 'ap' would not be equivalent to '<*>'. The correct monad instance should be something like: instance Monad StreamList where return = pure xs >>= f = SL $ join $ fmap (toList . f) $ toList xs where join :: [[a]] -> [a] join [] = [] join ([]:xss) = join (map tail xss) join ((x:xs):xss) = x : join (map tail xss) where 'ap' does equal '<*>' (not tested nor proofed yet though). I think a good definition of "compatible" would be that forall mf mx. ap mf mx = mf <*> mx. So I would still like to see an example where a user defined, non-monadic '<*>' causes bugs because it's not compatible to the intrinsic one. Regards, Bas ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
> "Option 3 avoids that problem but risks perplexity: if I make use of > some cool package which introduces some Foo :: * -> *, I might notice > that Foo is a monad and add a Monad Foo instance in my own code, > expecting the Applicative Foo instance to be generated in concert; to > my horror, I find my code has subtle bugs because the package > introduced a different, non-monadic, Applicative Foo instance which > I'm accidentally using instead." > > talks about "subtle bugs". Could you give an example of such a bug? > > I would expect that the non-monadic Applicative Foo instance is always > somehow "compatible" with the monadic one. However I don't have a > clear definition of "compatible" yet... > I think it's something like that. Module Foo defines list and make ZipList-like Applicative instance. Would you add standard list monad you have a bug. But if you add monad instance which is not compatible with existing applicative you have bug whether you use extension or not. module Foo where data [a] = a : [a] | [] instance Functor [] where fmap = map instamce Applicative [] where pure = repeat (<*>) = zipWith ($) module Main where instance Monad [] where return x = [x] (>>=) = concatMap ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On 22 August 2011 10:10, Simon Peyton-Jones wrote: > | > I don't completely understant how does it work. Does client need to enable > | > language extension to get default instances? > | > | I think that the extension would only be required to *define them*, > | not for them to be generated. The more conservative choice would > | indeed be to require the extension for both, though. > > Yes. I've clarified > http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances to say > this. > > | > Also proposal cannot fix Functor/Applicative/Monad problem without > breaking > | > client code. It requires explicit opt-out but client may define > Applicative > | > instance. And unless "hiding" is added it will result in compile error. > | > | I think the intention (at least as I understand it) is that a > | superclass default is only used to generate an instance if there is > | not already some suitable instance in scope, just like a default > | method is only used if there is not some explicit definition for the > | method in the instance. > > Actually that is not what Conor and I proposed. See > http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances. Under > "Variations" we discuss the "silent-opt-out" choice. But it's bad enough > knowing exactly what instances are in scope (given that they are not named), > let alone having that control what further instances are or are not > generated! For superclass defaults there is no such ambiguity. > > Simon > > > ___ > Glasgow-haskell-users mailing list > Glasgow-haskell-users@haskell.org > http://www.haskell.org/mailman/listinfo/glasgow-haskell-users > Won't option 1 "Reject this as a duplicate instance declaration, which indeed it is." conflict with design goal 1: "a class C can be re-factored into a class C with a superclass S, without disturbing any clients"? Take the transformers package for example. It defines lot's of instances of Functor and Applicative for its monad transformers. Doesn't transformers have to be changed if we go for option 1 (by either dropping the Functor and Applicative instances or listing hiding clauses in the Monad instances) thereby seriously conflicting with the design goal of not disturbing any clients. I expected the semantics to be like option 3: "Allow the explicit to supersede the intrinsic default silently". It has the advantage of: 1) Not disturbing any client code. 2) Giving the ability to define optimized implementations if you're not happy with the default ones (without using the hiding mechanism). 3) Feeling very much like the semantics of default methods thereby conforming to the rule of least surprise. The argument against option 3, which I quote: "Option 3 avoids that problem but risks perplexity: if I make use of some cool package which introduces some Foo :: * -> *, I might notice that Foo is a monad and add a Monad Foo instance in my own code, expecting the Applicative Foo instance to be generated in concert; to my horror, I find my code has subtle bugs because the package introduced a different, non-monadic, Applicative Foo instance which I'm accidentally using instead." talks about "subtle bugs". Could you give an example of such a bug? I would expect that the non-monadic Applicative Foo instance is always somehow "compatible" with the monadic one. However I don't have a clear definition of "compatible" yet... Thanks, Bas ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
RE: Superclass defaults
| > I don't completely understant how does it work. Does client need to enable | > language extension to get default instances? | | I think that the extension would only be required to *define them*, | not for them to be generated. The more conservative choice would | indeed be to require the extension for both, though. Yes. I've clarified http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances to say this. | > Also proposal cannot fix Functor/Applicative/Monad problem without breaking | > client code. It requires explicit opt-out but client may define Applicative | > instance. And unless "hiding" is added it will result in compile error. | | I think the intention (at least as I understand it) is that a | superclass default is only used to generate an instance if there is | not already some suitable instance in scope, just like a default | method is only used if there is not some explicit definition for the | method in the instance. Actually that is not what Conor and I proposed. See http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances. Under "Variations" we discuss the "silent-opt-out" choice. But it's bad enough knowing exactly what instances are in scope (given that they are not named), let alone having that control what further instances are or are not generated! For superclass defaults there is no such ambiguity. Simon ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On 21 August 2011 21:03, Alexey Khudyakov wrote: > I don't completely understant how does it work. Does client need to enable > language extension to get default instances? I think that the extension would only be required to *define them*, not for them to be generated. The more conservative choice would indeed be to require the extension for both, though. > Also proposal cannot fix Functor/Applicative/Monad problem without breaking > client code. It requires explicit opt-out but client may define Applicative > instance. And unless "hiding" is added it will result in compile error. I think the intention (at least as I understand it) is that a superclass default is only used to generate an instance if there is not already some suitable instance in scope, just like a default method is only used if there is not some explicit definition for the method in the instance. So the "hiding" mechanism would only be required to work around tricky edge cases with orphan instances. Max ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On 15.08.2011 14:36, Simon Peyton-Jones wrote: With help from Conor we have made some progress on this: we have a draft design: http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances If you care about the issue, maybe you can help resolve the above questions. In particular, to give concrete evidence for (b), working out some case studies would be a Good Thing. The examples given in other proposals using the extension proposed here would be one starting point. I don't completely understant how does it work. Does client need to enable language extension to get default instances? Also proposal cannot fix Functor/Applicative/Monad problem without breaking client code. It requires explicit opt-out but client may define Applicative instance. And unless "hiding" is added it will result in compile error. ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On 8/15/11 7:44 AM, Conor McBride wrote: This is a recurring issue, at least as far as mailing list heat is concerned, but it would be good to have more evidence of substantial impact. The Monoid vs Semigroup issue is a case in point, perhaps. Folks, what do you think? For my work it'd certainly be helpful to have Semigroup broken out as a superclass of Monoid. So far it hasn't been important enough to make me want to pull in the semigroups package, but adding it to base would help clean things up. One problem I see, though, is the question of what exactly is the "right" hierarchy to use. In particular, for the work I do, refactoring Monoid very quickly runs into the need for a Semiring class to replace/refactor Num, which opens an enormous can of worms. This is part of the reason why I continually bring up the fact that the various attempts at refactoring the numeric hierarchy fail, in that they ignore semirings and run off headlong towards fields and vector spaces (which are of less concern for my work; though I'd like to see modules in lieu of vector spaces). Of course, the real problem is that we are only allowed to have one instance for a given type, whereas semirings/rings/fields/etc are defined by having two instances which interlock in special ways. This means that a proper semiring/monoid/group hierarchy is going to be inherently at odds with a proper semiring/ring/field hierarchy. I've yet to come up with a solution to this conundrum which I actually like. -- Live well, ~wren ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On 8/15/11 7:44 AM, Conor McBride wrote: On 15 Aug 2011, at 11:36, Simon Peyton-Jones wrote: But, concerning proposed extensions to GHC about class aliases/superclass defaults etc, the truth is that the biggest reason for inertia here at GHC HQ is not so much the implementation effort (athough that is always an issue). Rather, it's uncertainty about (a) Is there a reasonably large group of users who really want such a change? Or is it just "nice to have"? (b) What is the "right" design? (c) Does it pay its way? (ie do the programming benefits justify the cost in terms of both language complexity and ongoing maintenance burden of one more feature to bear in mind when changing anything) [...] One thing that's really noticeable and sad about the status quo is that we can't refine the structure of a bunch of related classes without breaking established interfaces. I guess an interesting question might be what progress is effectively being blocked by our current inability to engineer around this problem. What are we stuck with just now? Right. IMO, this concern deserves to be added to the three that Simon mentioned. In particular, I think this is more to the point than (a), though clearly in the same spirit. We've known that the monadic and numeric towers have been in need of refinement for quite some time, but the status quo actively impedes resolving the issues with the current design. While the numeric-prelude, yap, and other packages have made a go at setting up new hierarchies, the status quo essentially forces people to buy into a single hierarchy for all their work because intercompatibility is just too hard. Due to package dependencies, this amounts to sending Haskell down the route of Lisp dialects, which adds additional compatibility problems to the original class hierarchy issues. Thus, the age-old response that people can set up their own class hierarchies has been shown not to work in practice. To my mind, this means that Simon's (a) or my/Conor's (a') is resolved with certainty. The remaining issues are the uncertainty with (b) and (c). Naturally the answer to (c) is going to depend on the design given in (b). So I suppose I ought to read the wiki page about the current proposal :) -- Live well, ~wren ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
> > My only input is that we have at least 2-3 (depending on whether the > latter two are to be considered separate) hierarchies in want of > refactoring: Functor/Applicative/Monad, Num and friends, and Monoid. > Ideally any solution would "solve the problem" for all of them, but > even if it doesn't (and only solves, say, Monad's case), I think it > should be a requirement that it at least allows for the others to be > solved as well in an incremental fashion afterwards (whether by > 'upgrading' the by-then existing feature, or adding a new, orthogonal > one). The undesirable scenario would be where you would have to > "change the world" all over again a second time to resolve the > remaining problems. > Another place where this might help would be with the RandomGen/SplittableGen issue: http://hackage.haskell.org/trac/ghc/ticket/4314 If "design goal 1" is met, then clients would not have to refactor their Random instance to match the new class factoring. -Ryan ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
On Mon, Aug 15, 2011 at 12:36 PM, Simon Peyton-Jones wrote: > (Adding GHC users, and changing title.) > > | Conor McBride wrote: > | > http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances > | > I don't know if it's likely to be implemented in GHC anytime soon,.. > | > So things are looking up. It should soon be technically feasible to > | > separate the issues of whether the Monoid operator should be (<>) and > | > whether it should actually live in a Semigroup superclass... > | > | Nice. But will it be happening soon, or not? And how soon is > | soon? > > Not soon enough to be useful for this mappend question. > > But, concerning proposed extensions to GHC about class aliases/superclass > defaults etc, the truth is that the biggest reason for inertia here at GHC HQ > is not so much the implementation effort (athough that is always an issue). > Rather, it's uncertainty about > > (a) Is there a reasonably large group of users who really want such a change? > Or is it just "nice to have"? > > (b) What is the "right" design? My only input is that we have at least 2-3 (depending on whether the latter two are to be considered separate) hierarchies in want of refactoring: Functor/Applicative/Monad, Num and friends, and Monoid. Ideally any solution would "solve the problem" for all of them, but even if it doesn't (and only solves, say, Monad's case), I think it should be a requirement that it at least allows for the others to be solved as well in an incremental fashion afterwards (whether by 'upgrading' the by-then existing feature, or adding a new, orthogonal one). The undesirable scenario would be where you would have to "change the world" all over again a second time to resolve the remaining problems. This is just in the abstract; my brain is too under-resourced to figure out where the proposed feature stands in relation to this. (On its own terms, I like it.) > > (c) Does it pay its way? (ie do the programming benefits justify the cost in > terms of > both language complexity and ongoing maintenance burden of one more > feature > to bear in mind when changing anything) > > With help from Conor we have made some progress on this: we have a draft > design: > http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances > > If you care about the issue, maybe you can help resolve the above questions. > In particular, to give > concrete evidence for (b), working out some case studies would be a Good > Thing. The examples given in other proposals using the extension proposed > here would be one starting point. > > If someone felt able to act as moderator for the discussion, willing to > summarise conclusions, open questions, and so on, on the wiki page, that > would be enormously helpful. I am in too many inner loops. But I *am* > willing to contemplate such an extension -- it has the same flavour as > default methods themselves, which are a very useful part of H98. > > Simon > > ___ > Libraries mailing list > librar...@haskell.org > http://www.haskell.org/mailman/listinfo/libraries > -- Work is punishment for failing to procrastinate effectively. ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Superclass defaults
[resend to GHC users, now I've subscribed!] Hi Simon On 15 Aug 2011, at 11:36, Simon Peyton-Jones wrote: | Nice. But will it be happening soon, or not? And how soon is | soon? Not soon enough to be useful for this mappend question. But, concerning proposed extensions to GHC about class aliases/ superclass defaults etc, the truth is that the biggest reason for inertia here at GHC HQ is not so much the implementation effort (athough that is always an issue). Rather, it's uncertainty about (a) Is there a reasonably large group of users who really want such a change? Or is it just "nice to have"? (b) What is the "right" design? (c) Does it pay its way? (ie do the programming benefits justify the cost in terms of both language complexity and ongoing maintenance burden of one more feature to bear in mind when changing anything) If you care about the issue, maybe you can help resolve the above questions. In particular, to give concrete evidence for (b), working out some case studies would be a Good Thing. The examples given in other proposals using the extension proposed here would be one starting point. I can't speak for "typical" users, but I can relay my own experience. I added this feature to SHE last time there was an outbreak of Functor/Applicative/Monad, just to focus a bit of concrete thought on the matter. Hacking on Epigram the other day, I finally got annoyed enough (with dumb extra instances) to use it, so I wired in default superclass instances Functor for Applicative, Applicative for Monad Functor for Traversable, Foldable for Traversable Alternative for MonadPlus and then went on the rampage! Nothing broke. I got rid of a lot of cruft. I did make essential use of hiding instance Functor for structures which were both Applicative and Traversable. I cut tens of lines for the cost of one or two hidings. If someone felt able to act as moderator for the discussion, willing to summarise conclusions, open questions, and so on, on the wiki page, that would be enormously helpful. I'm up for that role, if that's appropriate. I am in too many inner loops. But I *am* willing to contemplate such an extension -- it has the same flavour as default methods themselves, which are a very useful part of H98. One thing that's really noticeable and sad about the status quo is that we can't refine the structure of a bunch of related classes without breaking established interfaces. I guess an interesting question might be what progress is effectively being blocked by our current inability to engineer around this problem. What are we stuck with just now? This is a recurring issue, at least as far as mailing list heat is concerned, but it would be good to have more evidence of substantial impact. The Monoid vs Semigroup issue is a case in point, perhaps. Folks, what do you think? Cheers Conor PS Are there any other default superclass instances for standard classes that SHE could usefully bake in? Sadly, we can't purge redundant methods from standard classes just yet, but we can at least get rid of boilerplate instances. ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
RE: Superclass defaults
| > If someone felt able to act as moderator for the discussion, willing | > to summarise conclusions, open questions, and so on, on the wiki | > page, that would be enormously helpful. | | I'm up for that role, if that's appropriate. I'll take you up on that, thank you! I've added some "SLPJ note" notes to http://hackage.haskell.org/trac/ghc/wiki/DefaultSuperclassInstances which could perhaps do with clarifying. Simon PS: to avoid spam, maybe now we have advertised this thread, we should restrict it to ghc-users? The *current* library question about mappend etc can stay on libraries. ___ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users