Functor hierarchy proposal and class system extension proposal

Ben Millwood haskell at benmachine.co.uk
Wed Jan 5 01:48:27 CET 2011


On Tue, Jan 4, 2011 at 7:59 PM, Conor McBride
<conor at strictlypositive.org> wrote:
> Hi Ben
>
> On 4 Jan 2011, at 19:19, Ben Millwood wrote:
>
>> On Tue, Jan 4, 2011 at 1:21 PM, Conor McBride
>> <conor at strictlypositive.org> wrote:
>>>
>>> Jón's proposal was to improve the latter situation by allowing the
>>> subclass
>>> to specify a default (partial) implementation of a superclass. So we
>>> might
>>> write
>>>
>>> This, on its own, is not quite enough. For one thing, we need a way to
>>> switch it off. I should certainly be permitted to write something like
>>>
>>>  instance Applicative Blah where
>>>   return = ...
>>>   (<*>) = ...
>>>   hiding instance Functor Blah
>>
>> The use of 'hiding' here I'd object to, as it really isn't a good
>> description of what's going on.
>
> It's perhaps suboptimal. I chose "hiding" only because it's already
> a vaguely keywordy thing. It's only syntax. What's important is...

Yeah, it's not my main objection, I just thought it worth pointing out
:) obviously the explicit approach is better because 'deriving' is a
nicer keyword than 'hiding' :P

>
> ...the argument about what should be implicit or explicit, opt-in
> or opt-out. In this argument, I disagree with you.
>
> I'd much rather it was notationally cheaper to go with the supplied
> default, provided deviation from the default is also fairly cheap
> (but explicit).

There's a fair question in whether we want deviation from the default
at all (although I think the answer is probably yes). I think it's
reasonable that any type that is an instance of Monad be forced to
have ap = (<*>), for example, so really the only reason I can see we'd
want to be able to override those functions would be for efficiency. I
always thought it weird that the Haskell report worried about
efficiency (see: MR) so I don't know that it's not better to do that
sort of thing with inline pragmas, rewrite rules, or similar such
arrangements. But the trend has been to increase the number of methods
in classes (now *> is in Applicative and <$ in Functor) rather than
vice versa, so perhaps there are stronger benefits than I know - I'm
not used to dealing with performance-sensitive code.

> My plan also has the advantage of cheaper backward compatibility
> (for this and other (future) class splittings).
>
> Note that in my example, return had moved to Applicative, pure had
> been dumped, and a typical Monad instance would look like

aww, but pure is a better name than return :P (and the bikeshed should be pink!)

> instance Monad Maybe where
>  Just x >>= f = f x
>  Nothing >>= _ = Nothing
>  return = Just   -- where this implicitly opts into and extends the
>                  -- Applicative instance
>                  -- and also implicitly generates Functor
>
> We could not simply have said "deriving Applicative" here, because
> the default instance is incomplete. In general, one might want to
> override some but not all of the default instance, just as one
> does when default method implementations come from the class.

This is fair enough, although I don't think one proposal does this
particularly worse than another. I'm unclear now on what exactly the
'hiding' clause does - surely you either implicitly opt into an
instance or you don't. If you don't you must provide it somewhere else
in scope, since it's a superclass, in which case the implicit instance
would be overriden anyway, right?

Something to think about - sometimes superclass instances have
different contexts, e.g. instance Functor ((,) e) where [...] but
instance (Monoid w) => Monad ((,) w). In that case, imagine if the
Functor instance disappeared - there would be no error because one
would be supplied implicitly, but you'd suddenly get a message about
missing a Monoid instance for some type you used fmap on, which would
be unpleasant to debug. This is the sort of thing that makes me uneasy
about implicit instances, and though I appreciate there are some
important advantages to them, things like how they scope, where they
can be defined, and how stable they are to other changes, need careful
consideration, I think.

> There's a general engineering concern as well. The refactoring cost
> of splitting Applicative off as a lesser version of Monad, taking
> return, adding (<*>) derivable from (>>=) is much reduced by this
> choice. I'm sure it's not the only instance of a class we might
> discover is better split: the opt-in default reduces inertia to
> such design improvements.
>
> I'd certainly be happy with a different opt-out notation, but I
> would be unhappy if opting in (and overriding/extending) were
> made more complex than necessary to allow an opt-out default.

I think you're probably right, but I think these proposals are always
more complex than they look and it's good to make sure you've
considered all the alternatives :)

> All the best
>
> Conor
>
>



More information about the Haskell-prime mailing list