Derived Functor instance for void types

Edward Kmett ekmett at gmail.com
Mon Jan 16 20:09:24 UTC 2017


By current behavior, I was referring to the behavior of Void, not deriving.
I think the current deriving behavior on this front is suboptimal by the
reasoning given in my previous post.

-Edward

On Mon, Jan 16, 2017 at 1:51 PM, Ryan Scott <ryan.gl.scott at gmail.com> wrote:

> I think I mostly agree with everything you've said, but I'm not sure I
> understood all the details, so let me try to recap:
>
> > In the case it doesn't occur at all or occurs only positively, er... we
> don't care. We're not case analyzing it =)
>
> Indeed there's only one stock class you can derive where the type
> occurs only positively, and that's Read, which already has nice
> behavior for empty datatypes:
>
>     data Empty
>     deriving instance Read Empty
>     ====>
>     instance Read Empty where
>       readPrec = parens pfail
>
> So indeed, this an easy case.
>
> > In the case of it occurring in both positive and negative position we
> have the option to 'pass it through' to exchange the bottom. In the case of
> changing the behavior of Functor and the like we're not actually changing
> the definedness of the result, we're merely choosing between "equivalent"
> bottoms. This is a rather exceptional case, but the ability to preserve
> placed bottoms in a structure in case they have meaning to the user seems
> quite valuable.
>
> To be clear, this corresponds to David's proposal to use -XEmptyCase,
> right?
>
> > In the case of negative position only, the current behavior is more
> defined than the stricter behavior Ryan proposes. I personally very much
> favor keeping Eq, Ord, etc. as defined as possible for Void, V1 and the
> like with the existing behavior. 'a' occurs in negative position only for
> these cases.
>
> What do you mean by "the current behavior" here? For instance, if you had:
>
>     data Empty
>     deriving instance Eq Empty
>
> Are you defining "the current behavior" to mean this? That is, what
> GHC currently spits out today:
>
>     instance Eq Empty where
>       (==) = error "Void =="
>
> Or by "current behavior", do you mean the (manually written) instance
> that Void has:
>
>     instance Eq Void where
>       _ == _ = True
>
> These two instances are quite different, after all.
>
> > Similarly the existing practice of not doing wasted work and producing
> more defined results, also seems valuable. Aiming for "consistency" here
> seems to be pursuing a goal that doesn't actually help anyone and just
> makes stuff less defined.
>
> Fully agree.
>
> Ryan S.
>
> On Mon, Jan 16, 2017 at 12:55 PM, Edward Kmett <ekmett at gmail.com> wrote:
> > There are really four cases to consider for deriving purposes.
> >
> > Some type T occurs positively, negative, not at all, or in both.
> >
> > * In the case it doesn't occur at all or occurs only positively, er... we
> > don't care. We're not case analyzing it =)
> >
> > * In the case of negative position only, the current behavior is more
> > defined than the stricter behavior Ryan proposes. I personally very much
> > favor keeping Eq, Ord, etc. as defined as possible for Void, V1 and the
> like
> > with the existing behavior. 'a' occurs in negative position only for
> these
> > cases.
> >
> > * In the case of it occurring in both positive and negative position we
> have
> > the option to 'pass it through' to exchange the bottom. In the case of
> > changing the behavior of Functor and the like we're not actually changing
> > the definedness of the result, we're merely choosing between "equivalent"
> > bottoms. This is a rather exceptional case, but the ability to preserve
> > placed bottoms in a structure in case they have meaning to the user seems
> > quite valuable.
> >
> > Similarly the existing practice of not doing wasted work and producing
> more
> > defined results, also seems valuable. Aiming for "consistency" here
> seems to
> > be pursuing a goal that doesn't actually help anyone and just makes stuff
> > less defined.
> >
> > -Edward
> >
> > On Mon, Jan 16, 2017 at 9:25 AM, Ryan Scott <ryan.gl.scott at gmail.com>
> wrote:
> >>
> >> To be clear, if you have:
> >>
> >>   data V a deriving Functor
> >>
> >> David is proposing in #13117 [1] that the derived instance use
> >> -XEmptyCase instead of the current error-based implementation:
> >>
> >>   instance Functor V where
> >>    fmap _ x = case x of {}
> >>
> >> This seems fine and well, but given that #13117 is basically a
> >> continuation of another discussion in #7401 [2], I feel like if we're
> >> going to tackle the issue of derived instances for empty data
> >> declaration, we should solve it for all stock classes.
> >>
> >> In particular, there are two thorny issues to consider here. What if
> >> we have this instead?
> >>
> >>   data V2 deriving Eq
> >>
> >> What instance should this produce? Reid Barton proposed in #10577 [3]
> >> that it should be:
> >>
> >>   instance Eq V2 where
> >>     a == _ = case a of {}
> >>
> >> But we have a choice here, since (==) has multiple arguments! The
> >> definition could also conceivably be:
> >>
> >>   instance Eq V2 where
> >>     _ == b = case b of {}
> >>
> >> Is there a uniform policy we can decide for functions with multiple
> >> arguments like this? In fmap, it's unambiguous since there's only one
> >> argument of type f a.
> >>
> >> Another issue to consider is that if we adopted this convention for
> >> derived Eq instances for empty datatypes, we'd actually be going
> >> against the convention set for Data.Void. As noted in [4], this is the
> >> current Eq instance for Void:
> >>
> >>   instance Eq Void where
> >>     _ == _ = True
> >>
> >> I'm not proposing that we change this definition, since there are many
> >> good reasons to have it this way (see the thread in [5] for Edward
> >> Kmett's convincing argument in favor of the current Eq Void instance).
> >> Rather, I'm asking if we would be OK with having this
> >> discrepancy--that is, deriving Eq for your own Void2 type would
> >> produce a different instance. Personally, I'd be fine with it, but I
> >> think we should ask for the community's input as well.
> >>
> >> So sorry to hijack this thread, David, but we really should answer
> >> these two questions as well:
> >>
> >> 1. What do we do in ambiguous cases like derived (==) implementations
> >> for empty datatypes?
> >> 2. Are we fine with derived instances for empty datatypes sometimes
> >> being different than the corresponding instances for Data.Void?
> >>
> >> Ryan S.
> >> -----
> >> [1] https://ghc.haskell.org/trac/ghc/ticket/13117
> >> [2] https://ghc.haskell.org/trac/ghc/ticket/7401#comment:46
> >> [3] https://ghc.haskell.org/trac/ghc/ticket/10577
> >> [4] https://mail.haskell.org/pipermail/libraries/2015-July/025959.html
> >> [5] https://mail.haskell.org/pipermail/libraries/2015-July/025965.html
> >> _______________________________________________
> >> Libraries mailing list
> >> Libraries at haskell.org
> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
> >
> >
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20170116/c2c4cfb0/attachment.html>


More information about the Libraries mailing list