Opened 3 years ago

Last modified 11 months ago

#13465 new bug

Foldable deriving treatment of tuples is too surprising

Reported by: dfeuer Owned by:
Priority: normal Milestone: 8.10.1
Component: Compiler Version: 8.1
Keywords: deriving Cc: twanvl
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Other Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

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

I think the right thing is probably to refuse to derive Foldable if components of a tuple other than the last one may contain the type variable we're looking for.

Change History (8)

comment:1 Changed 3 years ago by hvr

Same goes for -XDeriveFunctor btw


PS:

Also, if you define a tuple data-type yourself, e.g.

Prelude> data T2 a b = T2 a b deriving (Show,Functor,Foldable)
Prelude> length (T2 () ())
1

GHC will fail refuse to derive Functor or Foldable if you use that one instead of the magic (,):

Prelude> data Pair a = Pair (T2 a a) deriving (Show,Functor,Foldable)
<interactive>:7:44: error:
    • Can't make a derived instance of ‘Functor Pair’:
        Constructor ‘Pair’ must use the type variable only as the last argument of a data type
    • In the data declaration for ‘Pair’


PS2:

Fun-fact: in GHCs prior to GHC 7.8, there was a nasty bug:

GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :set -XDeriveFoldable 
Prelude> data Foo a = Foo ((a,a),a) deriving Data.Foldable.Foldable
Prelude> Data.Foldable.toList $ Foo ((1,2),3)
[1,2,2]
Last edited 3 years ago by hvr (previous) (diff)

comment:2 in reply to:  1 Changed 3 years ago by dfeuer

Replying to hvr:

Same goes for -XDeriveFunctor btw

DeriveFunctor will only surprise by succeeding when might not expect it to; it cannot really have surprising behavior.

comment:3 Changed 3 years ago by RyanGlScott

Cc: twanvl added

Ick. This is an eccentricity of DeriveFunctor/DeriveFoldable/DeriveTraversable that I've never been keen on fixing, but since folks are complaining about it, I suppose it should at least be addressed.

It's a somewhat obscure piece of GHC trivia that DeriveFunctor et al. special-cases two type constructors:

  1. (->)
  2. Tuples

The (->) special case is somewhat justifiable, as it's quite common to define Functor instances where the type parameter occurs to the left of an arrow. The tuples special-casing is more confusing to me, as I'm not sure what practical purpose it serves. I've cc'd twanvl, the original author of these deriving extensions, in case he can share some insight on why he chose to implement it that way.

I too think the Right Thing™ to do here is to change the behavior so that we just call fmap/foldMap/traverse on occurrences of tuple types, and reject things like data Foo a = Foo (a, a) deriving (Functor, Foldable, Traversable). But there's one thorny corner case to watch out for: unboxed tuples. An even more obscure piece of GHC trivia is that you can do this:

data Foo a = Foo (# a, a #) deriving (Functor, Foldable)

This is only possible because of special-casing, as calling fmap/foldMap on an entire unboxed tuple is ill-kinded (unless we were to adopt levity-polymorphic versions of Functor and Foldable, I suppose). Moreover, it would feel weird to allow data Foo a = Foo (# a, a #) but not data Foo a = Foo (a, a). I suppose we could tweak the special-casing so that we reject unboxed tuples where the type parameter occurs somewhere other than the last field, which would at least make its behavior consistent with that of boxed tuples.

But the biggest roadblock by far is that this would (1) make fewer programs compile than before, and (2) change the behavior of existing programs. I think we should definitely get some kind of community consensus (i.e., a GHC proposal) before marching forth with this, although I have a funny feeling that discussing the behavior of deriving Foldable on tuple types is not going to go well...

comment:4 Changed 3 years ago by RyanGlScott

Sorry, the claim that the behavior of existing programs would change is false—it would only cause some existing programs that currently compile to no longer compile, which I think is acceptable.

Also, here's the ghc-devs mailing list discussion on the matter.

comment:5 Changed 2 years ago by RyanGlScott

Keywords: deriving added

comment:6 Changed 22 months ago by bgamari

Milestone: 8.4.18.6.1

This ticket won't be resolved in 8.4; remilestoning for 8.6. Do holler if you are affected by this or would otherwise like to work on it.

comment:7 Changed 17 months ago by bgamari

Milestone: 8.6.18.8.1

These will not be addressed in GHC 8.6.

comment:8 Changed 11 months ago by osa1

Milestone: 8.8.18.10.1

Bumping milestones of low-priority tickets.

Note: See TracTickets for help on using tickets.