GHC: Ticket #7436: Derived Foldable and Traversable instances become extremely inefficient due to eta-expansion
http://ghc.haskell.org/trac/ghc/ticket/7436
<p>
The following program:
</p>
<pre class="wiki">{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Prelude hiding (foldr)
import Data.Foldable
data List a = Nil | Cons a (List a)
deriving (Functor, Foldable)
mkList :: Int -> List Int
mkList 0 = Nil
mkList n = Cons n (mkList (n-1))
main :: IO ()
main = print $ foldr (\x y -> y) "end" (mkList n)
where n = 100000
</pre><p>
Takes <code>n^2</code> time to run with GHC 7.6.1 -O2.
</p>
<p>
The generated <code>Foldable</code> code looks something like this:
</p>
<pre class="wiki">instance Foldable List where
foldr f z Nil = z
foldr f z (Cons x xs) = f x (foldr (\a b -> f a b) z xs)
</pre><p>
Eta-reducing the function, i.e.
</p>
<pre class="wiki">instance Foldable List where
foldr f z Nil = z
foldr f z (Cons x xs) = f x (foldr f z xs)
</pre><p>
Makes the program linear in <code>n</code> (in this case, runtime goes from 8.160s to 0.004s).
</p>
<p>
The <code>Traversable</code> instance also has the same issue.
</p>
<p>
There seem to be three different issues:
</p>
<ul><li>Derived <code>Foldable</code> and <code>Traversable</code> instances are nearly unusable for large structures.
</li></ul><ul><li>An eta-expanded definition like <code>foldr</code> becomes asymptotically worse for some reason. Maybe this is expected behavior for this function, since <code>f</code> gets eta-expanded at each iteration?
</li></ul><ul><li><code>Foldable</code> instances are generated with <code>foldr</code> instead of <code>foldMap</code>.
</li></ul><p>
This isn't directly related, since the code would have the same problem either
way, but since I'm already writing about it... <code>foldMap</code> can allow
asymptotically better operations on a structure than <code>foldr</code> (for example,
finding the rightmost leaf of a binary tree using <code>Data.Monoid.Last</code>), so it
should probably be generated instead. A <code>foldMap</code> definition should look like a
simpler version of <code>traverse</code>, which is already derivable. Maybe this should be
a separate ticket.
</p>
en-usGHChttp://ghc.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://ghc.haskell.org/trac/ghc/ticket/7436
Trac 1.2.2simonpjWed, 21 Nov 2012 13:32:58 GMTdescription changed; difficulty set
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:1
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:1
<ul>
<li><strong>difficulty</strong>
set to <em>Unknown</em>
</li>
<li><strong>description</strong>
modified (<a href="/trac/ghc/ticket/7436?action=diff&version=1">diff</a>)
</li>
</ul>
TicketsimonpjWed, 21 Nov 2012 13:36:15 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:2
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:2
<p>
I'm very surprised that the eta-expanded version has asymptotically worse behaviour.
</p>
<p>
Could you generate an example with hand-written code (not generated by <code>deriving</code>) that elicits this behaviour? I'm gueesing that'd be simple do to, and it'd save wading through mountains of irrelevant Core. The smaller and more standalone the test case, the better.
</p>
<p>
I'm vaguely wondering whether it might be a space issue; ie takes lots of space for chains of partial applications etc. Maybe you can use <code>+RTS -s</code> for each version and give us the output. Thanks!
</p>
<p>
Simon
</p>
TicketshachafWed, 21 Nov 2012 13:55:56 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:3
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:3
<p>
The two instances I gave work as as they are without deriving (no Core
necessary -- the code is a simplified version of what <code>-ddump-deriv</code>
generated). Here's a simpler version without type classes (or lists):
</p>
<pre class="wiki">data Nat = Z | S Nat
mkNat :: Int -> Nat
mkNat 0 = Z
mkNat n = S (mkNat (n-1))
unNat :: Nat -> ()
unNat Z = ()
unNat (S n) = unNat n
fast :: (b -> b) -> b -> Nat -> b
fast s z Z = z
fast s z (S n) = s (fast s z n)
slow :: (b -> b) -> b -> Nat -> b
slow s z Z = z
slow s z (S n) = s (slow (\e -> s e) z n)
main :: IO ()
--main = print $ unNat . fast S Z . mkNat $ n
main = print $ unNat . slow S Z . mkNat $ n
where n = 100000
</pre><p>
And the <code>+RTS -s</code> output for both:
</p>
<ul><li>Fast:
</li></ul><pre class="wiki">shachaf@carbon:~/9$ ghc -rtsopts -O2 C.hs && time ./C +RTS -s
[1 of 1] Compiling Main ( C.hs, C.o )
Linking C ...
()
9,651,768 bytes allocated in the heap
10,904 bytes copied during GC
44,416 bytes maximum residency (2 sample(s))
21,120 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 17 colls, 0 par 0.00s 0.00s 0.0000s 0.0000s
Gen 1 2 colls, 0 par 0.00s 0.00s 0.0001s 0.0002s
INIT time 0.00s ( 0.00s elapsed)
MUT time 0.00s ( 0.00s elapsed)
GC time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 0.00s ( 0.00s elapsed)
%GC time 0.0% (9.8% elapsed)
Alloc rate 2,412,942,000 bytes per MUT second
Productivity 100.0% of total user, 116.6% of total elapsed
real 0m0.005s
user 0m0.004s
sys 0m0.000s
</pre><ul><li>Slow:
</li></ul><pre class="wiki">shachaf@carbon:~/9$ ghc -rtsopts -O2 C.hs && time ./C +RTS -s
[1 of 1] Compiling Main ( C.hs, C.o )
Linking C ...
()
11,251,768 bytes allocated in the heap
4,122,872 bytes copied during GC
1,223,248 bytes maximum residency (3 sample(s))
528,816 bytes maximum slop
4 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 19 colls, 0 par 0.00s 0.00s 0.0002s 0.0002s
Gen 1 3 colls, 0 par 0.00s 0.00s 0.0006s 0.0016s
INIT time 0.00s ( 0.00s elapsed)
MUT time 8.23s ( 8.25s elapsed)
GC time 0.00s ( 0.01s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 8.24s ( 8.25s elapsed)
%GC time 0.0% (0.1% elapsed)
Alloc rate 1,366,747 bytes per MUT second
Productivity 100.0% of total user, 99.8% of total elapsed
real 0m8.253s
user 0m8.237s
sys 0m0.000s
</pre>
TicketsimonpjWed, 21 Nov 2012 14:03:44 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:4
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:4
<p>
Thanks for the simple version. Truly bizarre. The slow code allocate ONLY 11 MBYTE, and yet takes EIGHT SECONDS to run. No time to look today, but if anyone cares to investigate that'd be great. (One thing to try would be profiling, though that might make the bug disappear.)
</p>
<p>
Simon
</p>
TicketshachafWed, 21 Nov 2012 14:28:09 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:5
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:5
<p>
I'd think what I said above about the eta-expansion happening at each iteration
is sufficient to explain it.
</p>
<p>
The reduction should happen something like this (not all at once, of course,
but this is enough to demonstrate what I mean):
</p>
<pre class="wiki">slow S Z
$ S (S (S (S Z)))
S $ slow (\a -> S a) Z
$ S (S (S Z))
S $ (\a -> S a)
$ slow (\b -> (\a -> S a) b) Z
$ S (S Z)
S $ (\a -> S a)
$ (\b -> (\a -> S a) b)
$ slow (\c -> (\b -> (\a -> S a) b)) Z
$ (S Z)
S $ (\a -> S a)
$ (\b -> (\a -> S a) b)
$ (\c -> (\b -> (\a -> S a) b))
$ slow (\d -> (\c -> (\b -> (\a -> S a) b))) Z
$ Z
S $ (\a -> S a)
$ (\b -> (\a -> S a) b)
$ (\c -> (\b -> (\a -> S a) b))
$ Z
</pre><p>
So we'd expect a tower of <code>1+2+3+...+n-1</code> reductions of the lambda.
</p>
<p>
The profiling results seem to agree (sum [1..100000-1] = 4999950000):
</p>
<pre class="wiki">shachaf@carbon:~/9$ ghc -prof -fprof-auto -rtsopts -O2 C.hs && time ./C +RTS -p
()
real 1m3.477s
user 1m3.372s
sys 0m0.004s
shachaf@carbon:~/9$ cat C.prof
Wed Nov 21 06:21 2012 Time and Allocation Profiling Report (Final)
C +RTS -p -RTS
total time = 49.32 secs (49319 ticks @ 1000 us, 1 processor)
total alloc = 11,249,528 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
slow.\ Main 100.0 14.2
slow Main 0.0 49.8
mkNat Main 0.0 35.6
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 45 0 0.0 0.0 100.0 100.0
main Main 91 0 0.0 0.1 0.0 0.1
CAF Main 89 0 0.0 0.0 100.0 99.6
main Main 90 1 0.0 0.0 100.0 99.6
mkNat Main 95 100001 0.0 35.6 0.0 35.6
slow Main 94 100001 0.0 49.8 100.0 64.0
slow.\ Main 96 4999950000 100.0 14.2 100.0 14.2
unNat Main 93 100001 0.0 0.0 0.0 0.0
main.n Main 92 1 0.0 0.0 0.0 0.0
CAF GHC.IO.Handle.FD 88 0 0.0 0.3 0.0 0.3
CAF GHC.Show 87 0 0.0 0.0 0.0 0.0
CAF GHC.Conc.Signal 86 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding 76 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding.Iconv 75 0 0.0 0.0 0.0 0.0
</pre>
TicketshachafWed, 21 Nov 2012 15:59:44 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:6
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:6
<p>
I just noticed that <code>DeriveFunctor</code> has the same issue, in a slightly less severe form (you have to look at all the elements to do quadratic work). "slow" is the instance that would be derived.
</p>
<pre class="wiki">data List a = Nil | Cons a (List a)
mkList :: Int -> List Int
mkList 0 = Nil
mkList n = Cons n (mkList (n-1))
sumList :: List Int -> Int
sumList = go 0
where
go a Nil = a
go a (Cons n ns) = a `seq` go (a+n) ns
slow :: (a -> b) -> List a -> List b
slow f Nil = Nil
slow f (Cons x xs) = Cons (f x) (slow (\e -> f e) xs)
fast :: (a -> b) -> List a -> List b
fast f Nil = Nil
fast f (Cons x xs) = Cons (f x) (fast f xs)
main :: IO ()
main = print $ sumList . slow id $ mkList n
where n = 100000
</pre>
TickettwanvlThu, 22 Nov 2012 10:37:55 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:7
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:7
<p>
The current deriving code uses 'holes'. For example, the fmap derivation has the type:
</p>
<pre class="wiki">ft_fmap :: FFoldType (LHsExpr RdrName -> State [RdrName] (LHsExpr RdrName))
</pre><p>
The deriviation takes an expresion (<code>LHsExpr RdrName</code>) to plug in the hole, and returns the total expression.
For example, <code>fmap f</code> for the type <code>"[a]"</code> is <code>\xs -> [|map f $xs|]</code>. Note that the lambda is at the meta level. This also means that we can't just use <code>f</code> itself, we have to use <code>\x -> [|f $x|]</code>.
</p>
<p>
When this is passed as an argument to a higher order function such as <code>fmap</code> or <code>foldr</code>, this lambda must be brought to the ast level. This is done by wrapping it in a concrete lambda. Since there is no way to tell wether the thing we are making concrete is in eta reducable form. Of course we could have a data type to capture eta-reducable expressions.
</p>
<p>
It would be much simpler to only have concrete lambdas. I.e. to derive <code>[|\xs -> map f xs|]</code> or without eta expanding, <code>[|map f|]</code>.
The only downside is that the generated code becomes a bit weirder. For example, for the type:
</p>
<pre class="wiki">data Foo a = Foo Int (Bar a) (a,a,a)
</pre><p>
we currently generate
</p>
<pre class="wiki">fmap f (Foo x y z) = Foo x (fmap (\a -> f a) y) (case z of (a,b,c) -> (f a, f b, f c))
</pre><p>
but this would become instead:
</p>
<pre class="wiki">fmap f (Foo x y z) = Foo ((\x' -> x') x) (fmap f y) ((\z' -> case z' of (a,b,c) -> (f a, f b, f c)) z)
</pre><p>
Similarly for foldr, where we now generate
</p>
<pre class="wiki">foldr f a0 (Foo x y z) = f x (foldr (\u v -> f u v) (case z of (a,b,c) -> f a (f b (f c a0))) y)
</pre><p>
this would become
</p>
<pre class="wiki">foldr f a0 (Foo x y z)
= f x
$ (\y' a0' -> foldr f a0' y') y
$ (\z' a0' -> case z' of (a,b,c) -> f a (f b (f c a0)) ) z
$ a0
</pre><p>
This shouldn't be a very difficult change, it might even simplify the code a bit. If I have some free time this weekend, and if I manage to get a working ghc build, I'll give it a try.
</p>
TicketparcsThu, 22 Nov 2012 14:15:49 GMTcc set
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:8
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:8
<ul>
<li><strong>cc</strong>
<em>patrick@…</em> added
</li>
</ul>
TickettwanvlFri, 23 Nov 2012 14:42:51 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/7436
http://ghc.haskell.org/trac/ghc/ticket/7436
<ul>
<li><strong>attachment</strong>
set to <em>0001-Changed-deriving-of-Functor-Foldable-Traversable-to-.patch</em>
</li>
</ul>
<p>
[PATCH] Changed deriving of Functor, Foldable, Traversable to fix <a class="closed ticket" href="http://ghc.haskell.org/trac/ghc/ticket/7436" title="#7436: bug: Derived Foldable and Traversable instances become extremely ... (closed: fixed)">#7436</a>. Added foldMap to derived Foldable instance.
</p>
TickettwanvlFri, 23 Nov 2012 14:45:12 GMTstatus changed
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:9
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:9
<ul>
<li><strong>status</strong>
changed from <em>new</em> to <em>patch</em>
</li>
</ul>
<p>
I have done what I described in my previous comment, and attached a patch.
</p>
<p>
I have also added foldMap to the generated Foldable instance, which I agree is a more natural function.
</p>
TicketsimonpjFri, 21 Dec 2012 16:19:24 GMTcc changed
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:10
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:10
<ul>
<li><strong>cc</strong>
<em>twanvl</em> <em>dreixel</em> added
</li>
</ul>
<p>
OK, let's wrap this one up.
</p>
<ul><li>Thank you for the patch. Are you confident that it's good to go? <strong>Pedro</strong> I believe that you were responsible for at least some of the <code>Traversable</code> deriving code; can you give an opinion?
</li></ul><ul><li>I wonder if you could add some comments to explain the construction? As you say, it may generate slightly strange code. Something like <code>Note [Avoid eta-expanded code]</code> with a compact explanation, an example, and a pointer to this ticket, would be useful.
</li></ul><ul><li>Are there are there any library changes?
</li></ul><ul><li>I looked at the eta-expansion thing. Yes, GHC is doing the right thing here, as you show. You might wonder why GHC doesn't eta-reduce <code>(\e -> s e)</code> to <code>s</code>. The reason is that doing so is unsound if <code>s</code> is bottom; then eta-reduction might turn a terminating program into a non-terminating one.
</li></ul><p>
Then I'll commit it.
</p>
<p>
Simon
</p>
TicketdreixelWed, 02 Jan 2013 13:29:30 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:11
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:11
<p>
I don't think I ever wrote any of the deriving code apart from <code>Generic</code> and <code>Typeable</code>.
</p>
TickettwanvlWed, 02 Jan 2013 13:50:17 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:12
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:12
<p>
I wrote the original deriving code for Functor/Foldable/Traversable. Someone else later cleaned it up a bit.
</p>
<p>
The strange code that is generated are lambdas applied to arguments, which is something that GHC should be able to optimize away easily. To avoid the eta-expansion, the generated code is always a function. So you end up with code like
</p>
<pre class="wiki">data Cons a = Cons (a,a)
fmap f (Cons x) = (\x -> case x of (x1,x2) -> (f x1 , f x2)) x
</pre><p>
I could add this example to a note, if you insist.
</p>
<p>
There are no library changes in the patch, and the derived code is functionally equivalent to what was there before.
</p>
TicketsimonpjWed, 02 Jan 2013 14:55:01 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:13
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:13
<p>
Yes, I think it'd be very helpful to add a <code>Note [Avoid eta-expanded code]</code>, explaining the problem, giving an example, and pointing to this ticket.
</p>
<p>
Thank you.
</p>
<p>
Simon
</p>
TickettwanvlThu, 03 Jan 2013 15:32:59 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:14
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:14
<p>
I added an explanation of the behaviour and a link to this ticket to the description at the top of the Functor deriving code. I did not make it a separate note, since there is no reference to it from inside the code. The patch that adds this note is attached.
</p>
TickettwanvlThu, 03 Jan 2013 15:33:18 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/7436
http://ghc.haskell.org/trac/ghc/ticket/7436
<ul>
<li><strong>attachment</strong>
set to <em>0002-Added-note-explaining-the-lambdas-generated-by-funct.patch</em>
</li>
</ul>
TicketsimonpjFri, 04 Jan 2013 10:50:27 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:15
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:15
<p>
Thanks. I'll apply.
</p>
Tickettwanvl@…Mon, 07 Jan 2013 10:48:14 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:16
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:16
<p>
commit <a class="changeset" href="http://ghc.haskell.org/trac/ghc/changeset/49ca2a37bef18aa57235ff1dbbf1cc0434979b1e/ghc" title="Changed deriving of Functor, Foldable, Traversable to fix #7436. Added ...">49ca2a37bef18aa57235ff1dbbf1cc0434979b1e</a>
</p>
<pre class="wiki">Author: Twan van Laarhoven <twanvl@gmail.com>
Date: Fri Nov 23 15:03:45 2012 +0100
Changed deriving of Functor, Foldable, Traversable to fix #7436. Added foldMap to derived Foldable instance.
The derived instances will no longer eta-expand the function. I.e. instead of
fmap f (Foo a) = Foo (fmap (\x -> f x) a)
we now derive
fmap f (Foo a) = Foo (fmap f a)
Some superflous lambdas are generated as a result. For example
data X a = X (a,a)
fmap f (X x) = (\y -> case y of (a,b) -> (f a, f b)) x
The optimizer should be able to simplify this code, as it is just beta reduction.
The derived Foldable instance now includes foldMap in addition to foldr.
compiler/prelude/PrelNames.lhs | 9 ++-
compiler/typecheck/TcGenDeriv.lhs | 178 ++++++++++++++++++++++---------------
2 files changed, 114 insertions(+), 73 deletions(-)
</pre>
TicketsimonpjMon, 07 Jan 2013 11:38:35 GMTstatus changed; testcase, resolution set
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:17
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:17
<ul>
<li><strong>status</strong>
changed from <em>patch</em> to <em>closed</em>
</li>
<li><strong>testcase</strong>
set to <em>perf/should_runt/T7436</em>
</li>
<li><strong>resolution</strong>
set to <em>fixed</em>
</li>
</ul>
<p>
Also:
</p>
<pre class="wiki">commit 3d51f271e6819c52508956f2426c4c19dec0b2fb
Author: Twan van Laarhoven <twanvl@gmail.com>
Date: Thu Jan 3 16:24:42 2013 +0100
Added note explaining the lambdas generated by functor deriving code, and how it compares to the old deriving code which used eta expansion.
</pre><p>
Thanks for the patches!
</p>
<p>
I added a test in <code>perf/should_run</code>. Interestingly the massive difference is run-time, not in allocation or residency.
</p>
<p>
Simon
</p>
TicketliyangThu, 10 Jan 2013 01:42:17 GMTcc changed
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:18
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:18
<ul>
<li><strong>cc</strong>
<em>hackage.haskell.org@…</em> added
</li>
</ul>
<p>
Since this issue affects a large number of users and doesn't seem too contentious, could this fix be included in 7.6.2?
</p>
TicketsimonpjThu, 10 Jan 2013 23:14:59 GMTstatus changed
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:19
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:19
<ul>
<li><strong>status</strong>
changed from <em>closed</em> to <em>merge</em>
</li>
</ul>
<p>
I'd be happy to see this in 7.6.2 if there is still time to merge it, Ian.
</p>
TicketjwlatoMon, 14 Jan 2013 00:41:57 GMTcc changed
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:20
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:20
<ul>
<li><strong>cc</strong>
<em>jwlato@…</em> added
</li>
</ul>
TicketiglooFri, 12 Apr 2013 19:37:46 GMTmilestone set
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:21
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:21
<ul>
<li><strong>milestone</strong>
set to <em>7.6.3</em>
</li>
</ul>
TicketsimonpjTue, 16 Apr 2013 15:55:15 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:22
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:22
<p>
This one is fixed, just awaiting merging into 7.6.3
</p>
TicketcarterFri, 20 Sep 2013 17:49:35 GMT
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:23
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:23
<p>
was this merged into 7.6?
else wise, should it be merged into 7.8/head?
</p>
TicketmonoidalSun, 22 Sep 2013 03:03:09 GMTstatus changed
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:24
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:24
<ul>
<li><strong>status</strong>
changed from <em>merge</em> to <em>closed</em>
</li>
</ul>
<p>
It has been fixed in HEAD long time ago, but was not merged into 7.6. Since 7.6.4 is not officially planned, I'm closing the ticket.
</p>
TicketRyanGlScottTue, 01 Aug 2017 16:41:57 GMTkeywords set
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:25
http://ghc.haskell.org/trac/ghc/ticket/7436#comment:25
<ul>
<li><strong>keywords</strong>
<em>deriving</em> added
</li>
</ul>
Ticket