Superclass defaults

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
30 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Superclass defaults

Simon Peyton Jones
(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?

 (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

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

RE: Superclass defaults

Simon Peyton Jones

| > 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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Conor McBride-2
In reply to this post by Simon Peyton Jones
[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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Gábor Lehel
In reply to this post by Simon Peyton Jones
On Mon, Aug 15, 2011 at 12:36 PM, Simon Peyton-Jones
<[hidden email]> 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
> [hidden email]
> http://www.haskell.org/mailman/listinfo/libraries
>



--
Work is punishment for failing to procrastinate effectively.

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Ryan Newton
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:


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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

wren ng thornton
In reply to this post by Conor McBride-2
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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

wren ng thornton
In reply to this post by Conor McBride-2
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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Alexey Khudyakov
In reply to this post by Simon Peyton Jones
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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Max Bolingbroke-2
On 21 August 2011 21:03, Alexey Khudyakov <[hidden email]> 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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

RE: Superclass defaults

Simon Peyton Jones
| > 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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Bas van Dijk-2
On 22 August 2011 10:10, Simon Peyton-Jones <[hidden email]> 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
> [hidden email]
> 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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Alexey Khudyakov
> "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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Bas van Dijk-2
On 29 August 2011 11:11, Aleksey Khudyakov <[hidden email]> 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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Jonas Almström Duregård-2
In reply to this post by Bas van Dijk-2
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 <[hidden email]> wrote:

> On 22 August 2011 10:10, Simon Peyton-Jones <[hidden email]> 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
>> [hidden email]
>> 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
> [hidden email]
> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
>

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Sebastian Fischer-2
In reply to this post by Bas van Dijk-2
On Mon, Aug 29, 2011 at 6:21 AM, Bas van Dijk <[hidden email]> 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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Victor Nazarov-2
In reply to this post by Simon Peyton Jones
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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

RE: Superclass defaults

Simon Peyton Jones
In reply to this post by Sebastian Fischer-2
| > 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: [hidden email] [mailto:glasgow-haskell-users-
| [hidden email]] On Behalf Of Sebastian Fischer
| Sent: 30 August 2011 03:49
| To: Bas van Dijk
| Cc: [hidden email]; Simon Peyton-Jones
| Subject: Re: Superclass defaults
|
| On Mon, Aug 29, 2011 at 6:21 AM, Bas van Dijk <[hidden email]> 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
| [hidden email]
| http://www.haskell.org/mailman/listinfo/glasgow-haskell-users


_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Jonas Almström Duregård-2
| > 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 <[hidden email]> 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: [hidden email] [mailto:glasgow-haskell-users-
> | [hidden email]] On Behalf Of Sebastian Fischer
> | Sent: 30 August 2011 03:49
> | To: Bas van Dijk
> | Cc: [hidden email]; Simon Peyton-Jones
> | Subject: Re: Superclass defaults
> |
> | On Mon, Aug 29, 2011 at 6:21 AM, Bas van Dijk <[hidden email]> 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
> | [hidden email]
> | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
>
>
> _______________________________________________
> Glasgow-haskell-users mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
>

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Conor McBride-2
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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Superclass defaults

Sebastian Fischer-2
In reply to this post by Simon Peyton Jones
On Wed, Aug 31, 2011 at 4:21 PM, Simon Peyton-Jones
<[hidden email]> 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
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
12