DeriveFoldable treatment of tuples is surprising

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

DeriveFoldable treatment of tuples is surprising

David Feuer
This seems much too weird:

*> :set -XDeriveFoldable
*> data Foo a = Foo ((a,a),a) deriving Foldable
*> length ((1,1),1)
1
*> length $ Foo ((1,1),1)
3

I've opened Trac #13465 [*] for this. As I write there, I think the
right thing is to refuse to derive Foldable for a type whose Foldable
instance would currently fold over components of a tuple other than
the last one.

I could go either way on Traversable instances. One could argue that
since all relevant components *must* be traversed, we should just go
ahead and do that. Or one could argue that we should be consistent
with Foldable and refuse to derive it.

What do you all think?

[*] https://ghc.haskell.org/trac/ghc/ticket/13465
_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: DeriveFoldable treatment of tuples is surprising

Jake McArthur

I think it's a question of what one considers consistent. Is it more consistent to treat tuples as transparent and consider every component with type `a`, or is it more consistent to treat tuples as opaque and reuse the existing Foldable instance for tuples even if it might cause a compile time error?


On Tue, Mar 21, 2017, 4:34 PM David Feuer <[hidden email]> wrote:
This seems much too weird:

*> :set -XDeriveFoldable
*> data Foo a = Foo ((a,a),a) deriving Foldable
*> length ((1,1),1)
1
*> length $ Foo ((1,1),1)
3

I've opened Trac #13465 [*] for this. As I write there, I think the
right thing is to refuse to derive Foldable for a type whose Foldable
instance would currently fold over components of a tuple other than
the last one.

I could go either way on Traversable instances. One could argue that
since all relevant components *must* be traversed, we should just go
ahead and do that. Or one could argue that we should be consistent
with Foldable and refuse to derive it.

What do you all think?

[*] https://ghc.haskell.org/trac/ghc/ticket/13465
_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users

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

Re: DeriveFoldable treatment of tuples is surprising

David Feuer
The point is that there are two reasonable ways to do it, and the
deriving mechanism, as a rule, does not make choices between
reasonable alternatives.

On Tue, Mar 21, 2017 at 5:05 PM, Jake McArthur <[hidden email]> wrote:

> I think it's a question of what one considers consistent. Is it more
> consistent to treat tuples as transparent and consider every component with
> type `a`, or is it more consistent to treat tuples as opaque and reuse the
> existing Foldable instance for tuples even if it might cause a compile time
> error?
>
>
> On Tue, Mar 21, 2017, 4:34 PM David Feuer <[hidden email]> wrote:
>>
>> This seems much too weird:
>>
>> *> :set -XDeriveFoldable
>> *> data Foo a = Foo ((a,a),a) deriving Foldable
>> *> length ((1,1),1)
>> 1
>> *> length $ Foo ((1,1),1)
>> 3
>>
>> I've opened Trac #13465 [*] for this. As I write there, I think the
>> right thing is to refuse to derive Foldable for a type whose Foldable
>> instance would currently fold over components of a tuple other than
>> the last one.
>>
>> I could go either way on Traversable instances. One could argue that
>> since all relevant components *must* be traversed, we should just go
>> ahead and do that. Or one could argue that we should be consistent
>> with Foldable and refuse to derive it.
>>
>> What do you all think?
>>
>> [*] https://ghc.haskell.org/trac/ghc/ticket/13465
>> _______________________________________________
>> Glasgow-haskell-users mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users
_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: DeriveFoldable treatment of tuples is surprising

Edward Kmett-2
As I recall, Richard Eisenberg has been pushing, off and on, for us to get a better vocabulary to specify "how" something is derived, via DeriveAnyClass, generalized newtype deriving, DeriveFoldable, etc.

In general I think the current behavior is the least surprising as it "walks all the a's it can" and is the only definition compatible with further extension with Traversable. Right now there are no instances provided by base that violate the "walk all the a's" intuition and there is a fair bit of user code for things like vector types that do things like

newtype V3 a = V3 (a,a,a,a)

replacing that with a data type isn't without cost because now converting back and forth between that and a tuple could no longer be done for zero cost with coercions. This style of code is more common among the ML-turned-haskeller crowd, whom -- in my experience -- tend to think of it as just giving the constructor paren around its arguments rather than as a tuple.

Destroying Foldable for that and making working code not work just for users to have to manually specify multiple tedious instances that should be easily derivable shouldn't be a thing we do lightly. DeriveFunctor doesn't consider that functors involved may be contravariant either. DeriveFoo generally does something that is a best effort.

I'm more inclined to leave it on the list of things that DeriveFoo does differently than GND, and as yet another argument pushing us to find a better vocabulary for talking about deriving.

-Edward


On Tue, Mar 21, 2017 at 5:11 PM, David Feuer <[hidden email]> wrote:
The point is that there are two reasonable ways to do it, and the
deriving mechanism, as a rule, does not make choices between
reasonable alternatives.

On Tue, Mar 21, 2017 at 5:05 PM, Jake McArthur <[hidden email]> wrote:
> I think it's a question of what one considers consistent. Is it more
> consistent to treat tuples as transparent and consider every component with
> type `a`, or is it more consistent to treat tuples as opaque and reuse the
> existing Foldable instance for tuples even if it might cause a compile time
> error?
>
>
> On Tue, Mar 21, 2017, 4:34 PM David Feuer <[hidden email]> wrote:
>>
>> This seems much too weird:
>>
>> *> :set -XDeriveFoldable
>> *> data Foo a = Foo ((a,a),a) deriving Foldable
>> *> length ((1,1),1)
>> 1
>> *> length $ Foo ((1,1),1)
>> 3
>>
>> I've opened Trac #13465 [*] for this. As I write there, I think the
>> right thing is to refuse to derive Foldable for a type whose Foldable
>> instance would currently fold over components of a tuple other than
>> the last one.
>>
>> I could go either way on Traversable instances. One could argue that
>> since all relevant components *must* be traversed, we should just go
>> ahead and do that. Or one could argue that we should be consistent
>> with Foldable and refuse to derive it.
>>
>> What do you all think?
>>
>> [*] https://ghc.haskell.org/trac/ghc/ticket/13465
>> _______________________________________________
>> Glasgow-haskell-users mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users
_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users


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

Re: DeriveFoldable treatment of tuples is surprising

Sven Panne-2
2017-03-21 22:29 GMT+01:00 Edward Kmett <[hidden email]>:
[... In general I think the current behavior is the least surprising as it "walks all the a's it can" and is the only definition compatible with further extension with Traversable. [...]

OTOH, the current behavior contradicts my intuition that wrapping a type into data/newtype plus using the deriving machinery is basically a no-op (modulo bottoms etc.). When I e.g. wrap a type t, I would be very surprised if the Eq/Ord instances of the wrapped type would behave differently than the one on t. I know that this is very handwavy argument, but I think the current behavior is *very* surprising.

Somehow the current behavior seems to be incompatible with the FTP, where pairs are given a special treatment (if that't the right/intuitive choice is a completely different topic, though).

Given the fact that "deriving Foldable" is quite old and therefore hard to change, I would at least suggest a big, fat warning in the documentation, including various examples where intuition and implementation do not necessarily meet.

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

Re: DeriveFoldable treatment of tuples is surprising

Oliver Charles-3
> there is a fair bit of user code for things like vector types that do things like

newtype V3 a = V3 (a,a,a,a)
         ^         1 2 3 4(!?)

Oh boy, I sure hope not ;)

On Wed, Mar 22, 2017 at 8:14 AM Sven Panne <[hidden email]> wrote:
2017-03-21 22:29 GMT+01:00 Edward Kmett <[hidden email]>:
[... In general I think the current behavior is the least surprising as it "walks all the a's it can" and is the only definition compatible with further extension with Traversable. [...]

OTOH, the current behavior contradicts my intuition that wrapping a type into data/newtype plus using the deriving machinery is basically a no-op (modulo bottoms etc.). When I e.g. wrap a type t, I would be very surprised if the Eq/Ord instances of the wrapped type would behave differently than the one on t. I know that this is very handwavy argument, but I think the current behavior is *very* surprising.

Somehow the current behavior seems to be incompatible with the FTP, where pairs are given a special treatment (if that't the right/intuitive choice is a completely different topic, though).

Given the fact that "deriving Foldable" is quite old and therefore hard to change, I would at least suggest a big, fat warning in the documentation, including various examples where intuition and implementation do not necessarily meet.
_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users

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

Re: DeriveFoldable treatment of tuples is surprising

Twan van Laarhoven
In reply to this post by David Feuer
On 2017-03-21 21:34, David Feuer wrote:
> This seems much too weird:
>
> *> :set -XDeriveFoldable
> *> data Foo a = Foo ((a,a),a) deriving Foldable
> *> length ((1,1),1)
> 1
> *> length $ Foo ((1,1),1)
> 3

This is not unique to tuples, consider:

     > :set -XDeriveFoldable
     > data Foo a = Foo [[a]] deriving Foldable
     > length [[1,2]]
     1
     > length $ Foo [[1,2]]
     2


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

Re: DeriveFoldable treatment of tuples is surprising

AntC
In reply to this post by David Feuer
> On Wed Mar 22 13:54:05 UTC 2017, Twan van Laarhoven wrote:

>> On 2017-03-21 21:34, David Feuer wrote:
>> This seems much too weird:
>>
>> *> :set -XDeriveFoldable
>> *> data Foo a = Foo ((a,a),a) deriving Foldable
>> *> length ((1,1),1)
>> 1
>> *> length $ Foo ((1,1),1)
>> 3

Hmm.
*> length $ Just ((1, 1), 1)
1
*> length $ Just (1, 1)
1
*> length (1, 1)
1


> This is not unique to tuples, consider:

>      > :set -XDeriveFoldable
>      > data Foo a = Foo [[a]] deriving Foldable
>      > length [[1,2]]
>      1
>      > length $ Foo [[1,2]]
>      2

       > length $ Just [[1, 2]]
      1

Does the behaviour of other methods within Foldable
seem surprising for DeriveFoldable Foo a = Foo ((a, a), a)?

Did the FTP change touch DeriveFoldable?
(Silly question, yes it must have: `length` didn't used to
be in Foldable.)

> On Tue Mar 21 21:29:20 UTC 2017, Edward Kmett wrote:
> In general I think the current behavior is the least
surprising as it
> "walks all the a's it can" and is the only definition
compatible with
> further extension with Traversable. Right now there are no
instances
> provided by base that violate the "walk all the a's"
intuition

?? Are there instances in base
where the `a` appears more than once on RHS?

I can see only List and Array.
How would I have formed that "walk all the a's" intuition?
(For most of the Prelude instances,
 `length`s result is hard-coded as 0 or 1.)

*> length (((1, 2), 3) :: Num a => ((a, a), a))
1
Doesn't seem to "walk all the a's" there.

I find pretty much all of these results surprising.
(Admittedly for different reasons in different cases.)

I think this is a good reason `length` should not be in
Foldable.
"lengthiness" just doesn't fit the abstraction.


AntC
_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/glasgow-haskell-users