Changes between Version 20 and Version 21 of IntrinsicSuperclasses


Ignore:
Timestamp:
Aug 5, 2014 2:32:32 PM (5 years ago)
Author:
pigworker
Comment:

more chat, arising from discussion

Legend:

Unmodified
Added
Removed
Modified
  • IntrinsicSuperclasses

    v20 v21  
    88
    99(More broadly, we have the recurrent problem of how to program defensively against progress. We may wish for benevolent evolution in the library, but we have to program against the library as it stands, and then the granting of our wishes breaks our code!)
     10
     11Moreover, whether refactoring a legacy hierarchy or not, some subclasses give rise to default definitions for some of their superclasses. E.g., `Ord` can induce a standard definition of `Eq`, `Monad` induces `Applicative`, `Applicative` and `Traversable` both induce `Functor`. In these cases, we commonly just want the obvious default implementations and it is a nuisance to spell them out longhand.
    1012
    1113=== Requirement 1: Some instance definitions in source code should generate multiple internal instances. ===
     
    8890
    8991Note that explicit `Functor` instances do not have a default implementation of `fmap` (that being rather the point of such instances), but that explicit `Applicative` and `Monad` would, under this proposal.
     92
     93The imagined solution delivers the instance of the intrinsic superclass by default, largely motivated by the refactoring issue, which has had nontrivial negative consequences for the evolution of the library, the Functor-Applicative-Monad hierarchy being a case in point. If we were gifted with foresight, we might prefer an "opt-in" approach, where default instances can be generated cheaply but not in total silence.
     94
     95Many default superclass instances are likely to define some but not all of the superclass members. E.g., we can make `return` a member of `Applicative`: we would then expect a `Monad` instance to define `return` but to acquire a default definition of `<*>`. An opt-in notation would need to (and could) say more than `deriving Applicative`.
    9096
    9197
     
    498504  * `deriving (Ord, Eq) gives `Ord a => Ord (Square a)` and `Eq a => Eq (Square a)`, with no pre-emption warning;
    499505  * `deriving (Ord - Eq)` gives just `Ord a => Ord (Square a)` requiring a separate hand-rolled `Eq (Square a)` instance.
     506
     507
     508-----------------------------------
     509
     510== Counterfactuals ==
     511
     512Arising from discussion, further modifications are worth considering, if only to step back from them.
     513
     514=== Liberalization 7 Use type inference to disambiguate member distribution. ===
     515
     516We could imagine permitting
     517{{{
     518class Foo x where
     519  foo :: x -> x
     520
     521class (instance Foo x, instance Foo y) => Goo x y where
     522  goo :: x -> y
     523}}}
     524and if we saw
     525{{{
     526instance Goo Int Bool where
     527  foo x = 3
     528  foo x = True
     529  goo = (0 <)
     530}}}
     531it would be obvious that we meant
     532{{{
     533instance Foo Int where
     534  foo x = 3
     535instance Foo Bool where
     536  foo x = True
     537instance Goo Int Bool where
     538  goo = (0 <)
     539}}}
     540because type information tells us which `foo` belongs where.
     541
     542That is, we could adopt a policy of complaining only if type inference fails to disambiguate the distribution of members to internal instances.
     543
     544We might need enough notation to opt out of `Foo x` but not `Foo y`. More explicit notation will be necessary
     545whenever the given definitions. E.g.,
     546{{{
     547instance Goo Int Bool where
     548  foo 0 = 3
     549  foo x = x
     550  foo x = True
     551  goo = (0 <)
     552}}}
     553does not establish in which instance the `foo x = x` line belongs.
     554
     555Of course, one could reject such programs as ambiguous, but certainly, the static semantics of such a system is far more subtle than the present proposal.
     556
     557=== Liberalization 8 Make superclasses intrinsic by default. ===
     558
     559We could drop the use of `instance` to mark which superclasses are intended as intrinsic. Again adopting a complain-on-ambiguity semantics, we could generate a superclass instance automatically from a subclass instance whenever there is at least one candidate member definition: either a default in the subclass, or an explicit definition in the subclass instance. So
     560{{{
     561class Bing x where
     562  bing :: x
     563class Bing x => Bong x where
     564  bong :: x
     565instance Bong Int where
     566  bing = 1
     567  bong = 0
     568}}}
     569generates a `Bing Int` instance too, as does
     570{{{
     571class Bing x where
     572  bing :: x
     573class Bing x => Bong x where
     574  bong :: x
     575  bing = bong
     576instance Bong Int where
     577  bong = 0
     578}}}
     579but
     580{{{
     581class Bing x where
     582  bing :: x
     583class Bing x => Bong x where
     584  bong :: x
     585instance Bong Int where
     586  bong = 0
     587}}}
     588generates only a `Bong Int` instance (rather than an empty `Bing Int` instance).
     589
     590Such a proposal would require a little more subtlety to determine which instances are generated, but might be sustainable.
     591
     592Certainly, it would still require authors of client code to be aware of whether a given class is likely to generate superclass instances. If I define some rather special purpose library with
     593{{{
     594class Eq x => WhizzBanger x where
     595  whizz :: x -> x -> x
     596  bang  :: x -> Bool
     597}}}
     598then clients need to know whether their `WhizzBanger` instances need to bring an `Eq` instance or will get one. This liberalization cuts the `instance` notation in class declarations but not the necessity to be aware of what it makes explicit.