Currently, if you write
data V a deriving Functor GHC generates fmap _ _ = error "Void fmap" This seems quite unfortunate, because it loses potentially useful error information: fmap (+ 3) (error "Too many snozzcumbers!") throws "Void fmap", rather than the much more precise "Too many snozzcumbers!" I've opened Trac #13117 to fix this, but I figured I should double check that no one is opposed. David Feuer _______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
That seems to have a surprising strictness. I'm not sure if it would be the best idea to try and evaluate a type with no inhabitants. On Sun, Jan 15, 2017 at 2:37 PM, David Feuer <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
I don't know what you mean. fmap for an uninhabited type is vacuously
strict: its result is always _|_. On Sun, Jan 15, 2017 at 11:00 PM, Kevin Cotrone <[hidden email]> wrote: > That seems to have a surprising strictness. > > I'm not sure if it would be the best idea to try and evaluate a type with no > inhabitants. > > On Sun, Jan 15, 2017 at 2:37 PM, David Feuer <[hidden email]> wrote: >> >> Currently, if you write >> >> data V a deriving Functor >> >> GHC generates >> >> fmap _ _ = error "Void fmap" >> >> This seems quite unfortunate, because it loses potentially useful error >> information: >> >> fmap (+ 3) (error "Too many snozzcumbers!") >> >> throws "Void fmap", rather than the much more precise "Too many >> snozzcumbers!" I've opened Trac #13117 to fix this, but I figured I should >> double check that no one is opposed. >> >> David Feuer >> >> _______________________________________________ >> Libraries mailing list >> [hidden email] >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
In reply to this post by Kevin Cotrone
"Preserving user bottoms" was found to be better behavior for us with Void as well back in the day. Evaluating such a term to get the bottom out is better than making up one that loses information for the user about precisely what bottom it is they had. We do so with absurd and the like for Void. This way if you map over a structure with errors at the leaves you get a new structure with those same errors at the leaves. tl;dr +1 from me. On Sun, Jan 15, 2017 at 11:00 PM, Kevin Cotrone <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
In reply to this post by David Feuer
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 [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
In reply to this post by David Feuer
And to be clear, if we can resolve all these questions, I'd volunteer
to implement the change to `deriving`'s behavior in GHC. Ryan S. _______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
In reply to this post by Ryan Scott
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 <[hidden email]> wrote: To be clear, if you have: _______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
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 <[hidden email]> 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 <[hidden email]> 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 >> [hidden email] >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
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 <[hidden email]> wrote: I think I mostly agree with everything you've said, but I'm not sure I _______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
Thanks for the clarification. I definitely agree that the current
error-based behavior for classes where T occurs in a negative position is suboptimal. If I understand your position correctly, then you advocate determining what the derived code for methods in which T occurs negatively on a case-by-case basis? How about I just list all of the stock derivable classes, give example instances, and see if you agree with them? data Empty a deriving (Eq, Ord, Read, Show, Functor, Foldable, Traversable, Lift, Generic, Data) ===> instance Eq (Empty a) where _ == _ = True instance Ord (Empty a) where compare _ _ = EQ instance Read (Empty a) where readPrec = parens pfail instance Show (Empty a) where showsPrec _ = absurd instance Functor Empty where fmap _ = absurd instance Foldable Empty where foldr _ z _ = z foldMap _ _ = mempty instance Traversable Empty where traverse _ = absurd instance Lift (Empty a) where lift = absurd instance Generic (Empty a) where from = absurd to = absurd instance Data (Empty a) where gfoldl _ = absurd gunfold _ _ c = constrIndex c of {} toConstr = absurd dataTypeOf = mkDataType "Empty" [] absurd :: Empty a -> b absurd x = case x of {} Note I didn't list Bounded, Enum, or Ix because GHC currently forbids you from deriving instances of those for empty datatypes, even with -XStandaloneDeriving on. Do you think we should revisit this? Ryan S. On Mon, Jan 16, 2017 at 3:09 PM, Edward Kmett <[hidden email]> wrote: > 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> >> > 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 >> >> [hidden email] >> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > >> > > > Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
I have no intuition about what Enum should do about void datatypes;
it's too broken. Bounded has one sensible default (minBound and maxBound should produce an error message naming the type and explain that it has no values). Ix gets pretty weird here. I would think range = const [] index _ x = case x of inRange _ _ = False OR inRange _ x = case x of {} (is it sensible to ask if _|_ is in an empty range?) rangeSize = const 0 On Mon, Jan 16, 2017 at 3:37 PM, Ryan Scott <[hidden email]> wrote: > Thanks for the clarification. I definitely agree that the current > error-based behavior for classes where T occurs in a negative position > is suboptimal. If I understand your position correctly, then you > advocate determining what the derived code for methods in which T > occurs negatively on a case-by-case basis? How about I just list all > of the stock derivable classes, give example instances, and see if you > agree with them? > > data Empty a deriving (Eq, Ord, Read, Show, Functor, Foldable, > Traversable, Lift, Generic, Data) > ===> > instance Eq (Empty a) where > _ == _ = True > instance Ord (Empty a) where > compare _ _ = EQ > instance Read (Empty a) where > readPrec = parens pfail > instance Show (Empty a) where > showsPrec _ = absurd > instance Functor Empty where > fmap _ = absurd > instance Foldable Empty where > foldr _ z _ = z > foldMap _ _ = mempty > instance Traversable Empty where > traverse _ = absurd > instance Lift (Empty a) where > lift = absurd > instance Generic (Empty a) where > from = absurd > to = absurd > instance Data (Empty a) where > gfoldl _ = absurd > gunfold _ _ c = constrIndex c of {} > toConstr = absurd > dataTypeOf = mkDataType "Empty" [] > > absurd :: Empty a -> b > absurd x = case x of {} > > Note I didn't list Bounded, Enum, or Ix because GHC currently forbids > you from deriving instances of those for empty datatypes, even with > -XStandaloneDeriving on. Do you think we should revisit this? > > Ryan S. > > On Mon, Jan 16, 2017 at 3:09 PM, Edward Kmett <[hidden email]> wrote: >> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> >>> > 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 >>> >> [hidden email] >>> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> > >>> > >> >> > _______________________________________________ > Libraries mailing list > [hidden email] > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
For reference, Data.Void.Void currently has no Enum or Bounded
instance. It does have an Ix instance [1]: instance Ix Void where range _ = [] index _ = absurd inRange _ = absurd rangeSize _ = 0 Ryan S. ----- [1] http://hackage.haskell.org/package/base-4.9.1.0/docs/src/Data.Void.html#line-53 On Mon, Jan 16, 2017 at 4:03 PM, David Feuer <[hidden email]> wrote: > I have no intuition about what Enum should do about void datatypes; > it's too broken. Bounded has one sensible default (minBound and > maxBound should produce an error message naming the type and explain > that it has no values). Ix gets pretty weird here. I would think > > range = const [] > index _ x = case x of > inRange _ _ = False OR inRange _ x = case x of {} (is it sensible > to ask if _|_ is in an empty range?) > rangeSize = const 0 > > On Mon, Jan 16, 2017 at 3:37 PM, Ryan Scott <[hidden email]> wrote: >> Thanks for the clarification. I definitely agree that the current >> error-based behavior for classes where T occurs in a negative position >> is suboptimal. If I understand your position correctly, then you >> advocate determining what the derived code for methods in which T >> occurs negatively on a case-by-case basis? How about I just list all >> of the stock derivable classes, give example instances, and see if you >> agree with them? >> >> data Empty a deriving (Eq, Ord, Read, Show, Functor, Foldable, >> Traversable, Lift, Generic, Data) >> ===> >> instance Eq (Empty a) where >> _ == _ = True >> instance Ord (Empty a) where >> compare _ _ = EQ >> instance Read (Empty a) where >> readPrec = parens pfail >> instance Show (Empty a) where >> showsPrec _ = absurd >> instance Functor Empty where >> fmap _ = absurd >> instance Foldable Empty where >> foldr _ z _ = z >> foldMap _ _ = mempty >> instance Traversable Empty where >> traverse _ = absurd >> instance Lift (Empty a) where >> lift = absurd >> instance Generic (Empty a) where >> from = absurd >> to = absurd >> instance Data (Empty a) where >> gfoldl _ = absurd >> gunfold _ _ c = constrIndex c of {} >> toConstr = absurd >> dataTypeOf = mkDataType "Empty" [] >> >> absurd :: Empty a -> b >> absurd x = case x of {} >> >> Note I didn't list Bounded, Enum, or Ix because GHC currently forbids >> you from deriving instances of those for empty datatypes, even with >> -XStandaloneDeriving on. Do you think we should revisit this? >> >> Ryan S. >> >> On Mon, Jan 16, 2017 at 3:09 PM, Edward Kmett <[hidden email]> wrote: >>> 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 <[hidden email]> 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 <[hidden email]> 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 <[hidden email]> >>>> > 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 >>>> >> [hidden email] >>>> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>>> > >>>> > >>> >>> >> _______________________________________________ >> Libraries mailing list >> [hidden email] >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
A possible derived fmap implementation for any empty data type could be `fmap _ = coerce`. This would ensure that any bottom was passed through. Of course this would run into problems with an explicit "type role" declaration on the empty data type. -- Eric On Mon, Jan 16, 2017 at 1:08 PM Ryan Scott <[hidden email]> wrote: For reference, Data.Void.Void currently has no Enum or Bounded _______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
Semantically, this is precisely equivalent to the empty case. Is there some reason to prefer it? On Jan 16, 2017 6:35 PM, "Eric Mertens" <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
It’s possible that empty case produces exactly the same eventual result. Simply looking at the core I was seeing a new bottom being potentially introduced. Given: data C a = D Void Void fmap1, fmap2 :: (a -> b) -> C a -> C b fmap1 _ = coerce fmap2 _ = \case {} I get: fmap4 = \ @ a_a2FE @ b_a2FF _ tpl_B2 -> tpl_B2 fmap1 = fmap4 `cast` ... fmap5 = \ @ b_a2FX -> patError "Demo.hs:12:11|case"# fmap2 = \ @ a_a2FW @ b_a2FX _ _ -> fmap5 And with the goal being the avoidance of new bottoms, fmap1’s generated core looked nicer.
_______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
fmap2 is not a legitimate implementation, and is only accepted without warning because of a type checker bug. C a is actually inhabited, by D undefined undefined. If you wrote data C a = D !Void !Void, that would be a different story, and would have no bottoms. fmap1 is legitimate, and completely equivalent, semantically, to the usual derived definition. On Jan 16, 2017 6:45 PM, "Eric Mertens" <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
Sorry, I meant a pattern checker bug, not a type checker bug. On Jan 16, 2017 6:55 PM, "David Feuer" <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
In reply to this post by Eric Mertens
Erm, silly question, but C isn't strictly an empty datatype, is it? If
you define C like so: data C a Then fmap2's Core looks pretty nice too: fmap2 = \ (@ a_arb) (@ b_arc) _ [Occ=Dead] (ds1_dId :: C a_arb) -> case ds1_dId of _ [Occ=Dead] { } On Mon, Jan 16, 2017 at 6:45 PM, Eric Mertens <[hidden email]> wrote: > It’s possible that empty case produces exactly the same eventual result. > Simply looking at the core I was seeing a new bottom being potentially > introduced. > > Given: > > data C a = D Void Void > > fmap1, fmap2 :: (a -> b) -> C a -> C b > fmap1 _ = coerce > fmap2 _ = \case {} > > I get: > > fmap4 = \ @ a_a2FE @ b_a2FF _ tpl_B2 -> tpl_B2 > fmap1 = fmap4 `cast` ... > fmap5 = \ @ b_a2FX -> patError "Demo.hs:12:11|case"# > fmap2 = \ @ a_a2FW @ b_a2FX _ _ -> fmap5 > > And with the goal being the avoidance of new bottoms, fmap1’s generated core > looked nicer. > > On Jan 16, 2017, at 3:38 PM, David Feuer <[hidden email]> wrote: > > Semantically, this is precisely equivalent to the empty case. Is there some > reason to prefer it? > > On Jan 16, 2017 6:35 PM, "Eric Mertens" <[hidden email]> wrote: >> >> A possible derived fmap implementation for any empty data type could be >> `fmap _ = coerce`. This would ensure that any bottom was passed through. Of >> course this would run into problems with an explicit "type role" declaration >> on the empty data type. >> >> -- >> Eric >> >> On Mon, Jan 16, 2017 at 1:08 PM Ryan Scott <[hidden email]> >> wrote: >>> >>> For reference, Data.Void.Void currently has no Enum or Bounded >>> instance. It does have an Ix instance [1]: >>> >>> instance Ix Void where >>> range _ = [] >>> index _ = absurd >>> inRange _ = absurd >>> rangeSize _ = 0 >>> >>> Ryan S. >>> ----- >>> [1] >>> http://hackage.haskell.org/package/base-4.9.1.0/docs/src/Data.Void.html#line-53 >>> >>> On Mon, Jan 16, 2017 at 4:03 PM, David Feuer <[hidden email]> >>> wrote: >>> > I have no intuition about what Enum should do about void datatypes; >>> > it's too broken. Bounded has one sensible default (minBound and >>> > maxBound should produce an error message naming the type and explain >>> > that it has no values). Ix gets pretty weird here. I would think >>> > >>> > range = const [] >>> > index _ x = case x of >>> > inRange _ _ = False OR inRange _ x = case x of {} (is it sensible >>> > to ask if _|_ is in an empty range?) >>> > rangeSize = const 0 >>> > >>> > On Mon, Jan 16, 2017 at 3:37 PM, Ryan Scott <[hidden email]> >>> > wrote: >>> >> Thanks for the clarification. I definitely agree that the current >>> >> error-based behavior for classes where T occurs in a negative position >>> >> is suboptimal. If I understand your position correctly, then you >>> >> advocate determining what the derived code for methods in which T >>> >> occurs negatively on a case-by-case basis? How about I just list all >>> >> of the stock derivable classes, give example instances, and see if you >>> >> agree with them? >>> >> >>> >> data Empty a deriving (Eq, Ord, Read, Show, Functor, Foldable, >>> >> Traversable, Lift, Generic, Data) >>> >> ===> >>> >> instance Eq (Empty a) where >>> >> _ == _ = True >>> >> instance Ord (Empty a) where >>> >> compare _ _ = EQ >>> >> instance Read (Empty a) where >>> >> readPrec = parens pfail >>> >> instance Show (Empty a) where >>> >> showsPrec _ = absurd >>> >> instance Functor Empty where >>> >> fmap _ = absurd >>> >> instance Foldable Empty where >>> >> foldr _ z _ = z >>> >> foldMap _ _ = mempty >>> >> instance Traversable Empty where >>> >> traverse _ = absurd >>> >> instance Lift (Empty a) where >>> >> lift = absurd >>> >> instance Generic (Empty a) where >>> >> from = absurd >>> >> to = absurd >>> >> instance Data (Empty a) where >>> >> gfoldl _ = absurd >>> >> gunfold _ _ c = constrIndex c of {} >>> >> toConstr = absurd >>> >> dataTypeOf = mkDataType "Empty" [] >>> >> >>> >> absurd :: Empty a -> b >>> >> absurd x = case x of {} >>> >> >>> >> Note I didn't list Bounded, Enum, or Ix because GHC currently forbids >>> >> you from deriving instances of those for empty datatypes, even with >>> >> -XStandaloneDeriving on. Do you think we should revisit this? >>> >> >>> >> Ryan S. >>> >> >>> >> On Mon, Jan 16, 2017 at 3:09 PM, Edward Kmett <[hidden email]> >>> >> wrote: >>> >>> 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 <[hidden email]> >>> >>> 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 <[hidden email]> >>> >>>> 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 >>> >>>> > <[hidden email]> >>> >>>> > 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 >>> >>>> >> [hidden email] >>> >>>> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> >>>> > >>> >>>> > >>> >>> >>> >>> >>> >> _______________________________________________ >>> >> Libraries mailing list >>> >> [hidden email] >>> >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> _______________________________________________ >>> Libraries mailing list >>> [hidden email] >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
In reply to this post by David Feuer
Yeah, I changed my example file along the way. Originally I intended not to have the D there.
_______________________________________________ Libraries mailing list [hidden email] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries |
Free forum by Nabble | Edit this page |