GHC: Ticket #1544: Derived Read instances for recursive datatypes with infix constructors are too inefficient
http://ghc.haskell.org/trac/ghc/ticket/1544
<p>
Consider this definition:
</p>
<pre class="wiki">data Exp = C | Exp :+: Exp | Exp :-: Exp deriving ( Read, Show )
</pre><p>
Now, try something like:
</p>
<pre class="wiki">> read "((((((((((C))))))))))" :: Exp
</pre><p>
Even this simple expression may take several seconds to parse. It gets worse if you keep adding parenthesis. And even worse if you add more infix constructors....
</p>
en-usGHChttp://ghc.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://ghc.haskell.org/trac/ghc/ticket/1544
Trac 1.0.7jcpetruzza@…Wed, 18 Jul 2007 05:26:35 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:1
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:1
<p>
I meant to write
</p>
<blockquote class="citation">
<p>
read "((((((((((C))))))))))" :: Exp
</p>
</blockquote>
TicketiglooFri, 17 Aug 2007 22:50:00 GMTmilestone set
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:2
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:2
<ul>
<li><strong>milestone</strong>
set to <em>6.8</em>
</li>
</ul>
TicketsimonpjMon, 20 Aug 2007 21:29:22 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/1544
http://ghc.haskell.org/trac/ghc/ticket/1544
<ul>
<li><strong>attachment</strong>
set to <em>ParseInfix.hs</em>
</li>
</ul>
TicketsimonpjMon, 20 Aug 2007 21:30:21 GMTdescription changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:3
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:3
<ul>
<li><strong>description</strong>
modified (<a href="/trac/ghc/ticket/1544?action=diff&version=3">diff</a>)
</li>
</ul>
<p>
Here is the story as far as I can work it out. I don't have a solution yet, but this records my diagnosis. I've asked Koen Claessen (who originated the clever parsing technology) for help. Here is his paper which describes it: <a class="ext-link" href="http://www.cs.chalmers.se/~koen/pubs/entry-jfp04-parser.html"><span class="icon"></span>http://www.cs.chalmers.se/~koen/pubs/entry-jfp04-parser.html</a>
</p>
<p>
For a data type like
</p>
<pre class="wiki">data Exp = C | Exp :+: Exp
</pre><p>
we get a parser like this:
</p>
<pre class="wiki"> readPrec = parens
((do Ident "C" <- lexP
return C)
+++
(prec 9 $
(do a <- step readPrec
Symbol ":+:" <- lexP
b <- step readPrec
return (a :+: b ))
))
</pre><p>
The trouble comes because when we see a paren we don't know whether it encloses the whole expression or just part
</p>
<pre class="wiki"> (C :+: C :+: C)
</pre><p>
or
</p>
<pre class="wiki"> (C :+: C) :+: C
</pre><p>
So the <tt>a<-readPrec</tt> line expands to a new unfolding of <tt>readPrec</tt>. We get two parsers working in parallel, one expecting the closing paren at the end,
</p>
<pre class="wiki"> (t1)
</pre><p>
where <tt>t1</tt> is a term, and one expecting a term of form
</p>
<pre class="wiki"> (t1) :+: t2
</pre><p>
(There are two more parsers, each expecting a plain C, but I'll ignore those. Also in case you are wondering, the precedence mechanism cuts off the left-recursion problem.)
</p>
<p>
So far so good. But when we consume a paren, each of these two parsers generate two new parsers in exactly the same way. So after consuming one paren we have FOUR parsers expecting terms of form
</p>
<pre class="wiki"> ((t1))
((t1) :+: t2)
((t1)) :+: t2
((t1) :+: t2) :+: t3
</pre><p>
This is clearly unacceptable, because after <tt>n</tt> parens we get <tt>2^n</tt> parsers. At least that is my theory, and it certainly would explain what is going on.
</p>
<p>
I have now forgotten why I originally adopted Koen's parallel parser idea! I'm sure it's a good one. But this does seem like a serious problem. Remember that we must generate a parser for data type <tt>T</tt> without looking at the parsers for any other data types.
</p>
<p>
Furthermore, although I said I'd ignore it, we do get two (or more) parsers each independently parsing the same lexeme.
</p>
<pre class="wiki"> (do { Ident "C" <- lexP; ...} +++ do { Ident "C" <- lexP; ...})
</pre><p>
They get expanded out, and then dynamically recombined by the parser machinery, but it seems like a lot of wasted work is done. But I don’t think that's what's giving the problem here.
</p>
<p>
I attach a complete program <tt>ParseInfix.hs</tt> that demonstrates the problem, with a hand-written read instance so that you don't have to guess what <tt>deriving</tt> is doing. Just compile and run.
</p>
<p>
Simon
</p>
TicketsimonpjMon, 20 Aug 2007 21:30:50 GMTdescription changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:4
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:4
<ul>
<li><strong>description</strong>
modified (<a href="/trac/ghc/ticket/1544?action=diff&version=4">diff</a>)
</li>
</ul>
Ticketjcpetruzza@…Tue, 21 Aug 2007 01:29:40 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/1544
http://ghc.haskell.org/trac/ghc/ticket/1544
<ul>
<li><strong>attachment</strong>
set to <em>alt-read.hs</em>
</li>
</ul>
<p>
Manual Read instance for Exp type
</p>
Ticketjcpetruzza@…Tue, 21 Aug 2007 01:46:10 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:5
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:5
<p>
Just in case it helps, I'm attaching the Read instance I defined to workaround this problem. The idea is to transform this grammar
</p>
<pre class="wiki">Exp :== C | Exp :+: Exp
</pre><p>
into
</p>
<pre class="wiki">Exp :== C (:+: Exp)*
</pre><p>
The resulting code is a bit longer, but seems to work fine and doesn't depend on any other types
</p>
TicketsimonpjMon, 17 Sep 2007 15:11:32 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:6
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:6
<p>
From Koen:
</p>
<p>
It was as I feared: Even the backtracking parser from the Haskell98
report has the exponential behavior you describe!
</p>
<p>
I took the code from Figure 8 here: <a class="ext-link" href="http://haskell.org/onlinereport/derived.html"><span class="icon"></span>http://haskell.org/onlinereport/derived.html</a>
</p>
<p>
And copied the relevant bits into a file:
</p>
<pre class="wiki">infixr 5 :^:
data Tree a = Leaf a | Tree a :^: Tree a
deriving ( Eq, Ord, Show )
instance (Read a) => Read (Tree a) where
readsPrec d r = readParen (d > up_prec)
(\r -> [(u:^:v,w) |
(u,s) <- readsPrec (up_prec+1) r,
(":^:",t) <- lex s,
(v,w) <- readsPrec (up_prec+1) t]) r
++ readParen (d > app_prec)
(\r -> [(Leaf m,t) |
("Leaf",s) <- lex r,
(m,t) <- readsPrec (app_prec+1) s]) r
up_prec = 5 :: Int -- Precedence of :^:
app_prec = 10 :: Int -- Application has precedence one more than
-- the most tightly-binding operator
</pre><p>
And then added the following:
</p>
<pre class="wiki">main = print (read s :: Tree Int)
where
s = "(((((((((((((((((((((((((((((((((Leaf 3"
++ ")))))))))))))))))))))))))))))))))"
</pre><p>
Neither Hugs nor GHC can evaluate this expression in reasonable time.
And (probably) neither would the old GHC without my ReadP/ReadPrec
stuff.
</p>
<p>
Conclusion: We need to be smarter when generating parsers for
datatypes with infix operators, <em>independent of</em> the underlying
parsing technology. In order to make an efficient parser for a grammar
with binary operators, one <em>must</em> massage the grammar to remove
left-recursion from the grammar.
</p>
TicketsimonpjMon, 17 Sep 2007 15:11:49 GMTcc set
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:7
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:7
<ul>
<li><strong>cc</strong>
<em>koen@…</em> added
</li>
</ul>
TicketsimonpjTue, 18 Sep 2007 07:58:12 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:8
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:8
<p>
Koen writes: Take the following example datatype.
</p>
<pre class="wiki"> data T = T :+: T | A
</pre><p>
Generating a naive recursive parser for this leads to the bad behavior.
</p>
<p>
However, for such simple recursive datatypes, we kind of know what
parsers we should generate. For example, forthe above datatype we
simply generate a parser that first parses a simple T, and then checks
if there is an occurrence of :+:, in which case it tries to parse
another T. Nice and linear.
</p>
<p>
However, we can never do this modularly. Here is why. Imagine a module with the following datatype:
</p>
<pre class="wiki"> data T a b = a :+: b deriving ( Read )
</pre><p>
The only thing we can do here is to generate the naive parser.
</p>
<p>
Imagine now another module that imports the above module, with a
datatype declaration that looks like:
</p>
<pre class="wiki"> data A = (T A A) :*: A
| C
deriving ( Read )
</pre><p>
Again, the only thing we can do here is to generate a naive parser,
that uses the parser we generated for T. (We do not even know that the
above datatype is recursive without looking at the definition of T.)
</p>
<p>
Now, the resulting parser will again have the bad behavior: Any
expression starting with many parentheses will lead to exponential
behavior, because, for each parenthesis, we do not know if it belongs
to something of type A or something of type T A A until we have parsed
the whole expression.
</p>
<p>
We <em>can</em> do something about recursive datatypes where the left
argument of the operators has exactly the same type as the whole
datatype. This works for:
</p>
<pre class="wiki"> data SnocList a = Nil | SnocList a :- a
</pre><p>
for example, but not for:
</p>
<pre class="wiki"> data T a = T [a] :+: T [a] | Leaf a
</pre><p>
I guess it is worth implementing this special case anyway, although it
will not apply to all cases.
</p>
<hr />
<p>
Here is the idea.
</p>
<p>
First a simple case. For the following Haskell code:
</p>
<pre class="wiki"> infix 5 +
infix 6 *
data A
= A + A
| A * A
| C B
</pre><p>
We generate the following grammar:
</p>
<pre class="wiki"> A(n) ::= A(6) "+" A(6) (n<=5)
| A(7) "*" A(7) (n<=6)
| "C" B(0)
| "(" A(0) ")"
</pre><p>
Right now, you simply turn the above into a parser directly. However,
we are going to "massage" the grammar a bit. First, we explicitly
split up the grammar into parts, depending on precedence. For this, we
need to sort and groups the operators according to precedences:
</p>
<pre class="wiki"> A(0..5) ::= A(6) "+" A(6)
| A(6)
A(6) ::= A(7) "*" A(7)
| A(7)
A(7..10) ::= "C" B(0)
| "(" A(0) ")"
</pre><p>
Then, we see that we have overlap in the first two grammar parts. We
get rid of it by using optional [ ]'s:
</p>
<pre class="wiki"> A(0..5) ::= A(6) ["+" A(6)]
A(6) ::= A(7) ["*" A(7)]
A(7..10) ::= "C" B(0)
| "(" A(0) ")"
</pre><p>
This can be turned into efficient Haskell code directly.
</p>
<hr />
<p>
Now a more complicated case. For the following Haskell code:
</p>
<pre class="wiki"> infix 5 +
infix 6 *
data A
= A + A
| B * A -- this operator is not left-recursive
| C B
</pre><p>
We generate the following grammar:
</p>
<pre class="wiki"> A(n) ::= A(6) "+" A(6) (n<=5)
| B(7) "*" A(7) (n<=6)
| "C" B(0)
| "(" A(0) ")"
</pre><p>
Right now, you simply turn the above into a parser directly. Again, we
are going to "massage" the grammar. First, we explicitly split up the
grammar into parts, depending on precedence.
</p>
<pre class="wiki"> A(0..5) ::= A(6) "+" A(6)
| A(6)
A(6) ::= B(7) "*" A(7)
| A(7)
A(7..10) ::= "C" B(0)
| "(" A(0) ")"
</pre><p>
Unfortunately, there is no (explicit) overlap in the cases for A(6).
We know that there probably exists overlap (both grammars will accept
any number of parentheses), but this is not clear, since we do not
have B's grammar.
</p>
<p>
We can get rid of some overlap by using optional [ ]'s:
</p>
<pre class="wiki"> A(0..5) ::= A(6) ["+" A(6)]
A(6) ::= B(7) "*" A(7)
| A(7)
A(7..10) ::= "C" B(0)
| "(" A(0) ")"
</pre><p>
This can be turned into Haskell code directly:
</p>
<pre class="wiki">readP n
| n <= 5 =
do x <- readP 6
return x +++ do "+" <- lex
y <- readP 6
return (x+y)
| n <= 6 =
do x <- readP 7 -- :: B
"*" <- lex
y <- readP 7
return (x*y)
+++ do readP 7 -- :: A
| otherwise =
do "C" <- lex
x <- readP 0
return (C x)
+++ do "(" <- lex
x <- readP 0
")" <- lex
return x
</pre><p>
However, this code will
be inefficient when parsing A(6)'s that start with lots of
parentheses.
</p>
TicketsimonpjMon, 12 Nov 2007 16:48:06 GMTcc, milestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:9
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:9
<ul>
<li><strong>cc</strong>
<em>doaitse@…</em> added
</li>
<li><strong>milestone</strong>
changed from <em>6.8 branch</em> to <em>6.10 branch</em>
</li>
</ul>
<p>
It's clear we aren't going to get this fixed in 6.8. There's actually an interesting research question here, about how to build parsers that are both modular and efficient.
</p>
<p>
Please would someone like to help with this?
</p>
<p>
(I've added Doaitse to the cc list; hope that's ok.)
</p>
<p>
Simon
</p>
TicketsimonpjTue, 13 Nov 2007 10:24:51 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:10
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:10
<p>
Doaitse writes "It is still in my mind, but I am not yet convinced that things are easily solvable. There might even be problems with a precise definition of the problem. I will try to cook up some examples with questions what one should like
to see happen."
</p>
Ticketmalcolm.wallace@…Tue, 13 Nov 2007 13:43:56 GMTcc changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:11
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:11
<ul>
<li><strong>cc</strong>
<em>malcolm@…</em> added
</li>
</ul>
TicketeelcoFri, 15 Feb 2008 12:29:42 GMTcc changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:12
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:12
<ul>
<li><strong>cc</strong>
<em>emlempsi@…</em> added
</li>
</ul>
TicketsimonpjMon, 18 Feb 2008 13:05:10 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/1544
http://ghc.haskell.org/trac/ghc/ticket/1544
<ul>
<li><strong>attachment</strong>
set to <em>Swiestra.pdf</em>
</li>
</ul>
<p>
Doaitse's draft paper that may help with this problem
</p>
TicketsimonpjMon, 18 Feb 2008 13:07:02 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:13
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:13
<p>
Great news! Doaitse writes "I have found a student with whom I will embark on the endeavour to construct a solution to this (I have some hopes); either we will have to show a solution, or we will have to prove that this is "impossible". I hope to be able to use the kind of transformation techniques we have developed in the attached paper".
</p>
<p>
Simon
</p>
TicketsimonpjThu, 01 May 2008 16:45:50 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:14
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:14
<p>
See <a class="ext-link" href="http://www.cs.uu.nl/wiki/bin/view/Center/TTTAS#The_internals_for_a_better_Deriv"><span class="icon"></span>The internals for a better Deriving Read and Write</a> which describes progress by Doaitse and his colleagues.
</p>
<p>
Simon
</p>
TicketsimonmarTue, 30 Sep 2008 15:37:21 GMTarchitecture changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:15
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:15
<ul>
<li><strong>architecture</strong>
changed from <em>Unknown</em> to <em>Unknown/Multiple</em>
</li>
</ul>
TicketsimonmarTue, 30 Sep 2008 15:51:26 GMTos changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:16
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:16
<ul>
<li><strong>os</strong>
changed from <em>Unknown</em> to <em>Unknown/Multiple</em>
</li>
</ul>
TicketiglooSat, 11 Apr 2009 23:13:28 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:17
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:17
<ul>
<li><strong>milestone</strong>
changed from <em>6.10 branch</em> to <em>6.12.1</em>
</li>
</ul>
<p>
Does anyone know what the status of this is?
</p>
TicketsimonpjTue, 14 Apr 2009 08:47:12 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:18
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:18
<p>
Doaitse, Marcos, and Eelco tackle precisely this issue:
</p>
<pre class="wiki">@inproceedings{1411296,
author = {Marcos Viera and S. Doaitse Swierstra and Eelco Lempsink},
title = {Haskell, do you read me?: constructing and composing
efficient top-down parsers at runtime},
booktitle = {Haskell '08: Proceedings of the first ACM SIGPLAN
symposium on Haskell},
year = {2008},
isbn = {978-1-60558-064-7},
pages = {63--74},
location = {Victoria, BC, Canada},
doi = {http://doi.acm.org/10.1145/1411286.1411296},
publisher = {ACM},
address = {New York, NY, USA},
}
</pre><p>
I'm not sure whether they regard their solution as suitable to directly incorporate in (say) GHC, but it's certainly a substantial contribution to this ticket!
</p>
<p>
Simon
</p>
TicketDoaitseTue, 14 Apr 2009 09:44:33 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:19
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:19
<p>
We consider this problem basically solved by the ChristmasTree library we uploaded to Hackage, with its associated packages.
</p>
<p>
See:
</p>
<p>
<a class="ext-link" href="http://hackage.haskell.org/packages/archive/ChristmasTree/0.1/doc/html/Text-GRead.html"><span class="icon"></span>http://hackage.haskell.org/packages/archive/ChristmasTree/0.1/doc/html/Text-GRead.html</a>
</p>
<p>
I am currently doubtful whether this should be incorporated in the GHC as it stands now. We think it is better to find some other uses of the TTTAS library before we decide how to proceed. Thus far we got no questions about these packages, which implies either that they solve the problem or that they are not used at all. In neither case this provides information to us on how to proceed. At least the severity could be lowered to "minor"?
</p>
<p>
Doaitse
</p>
TicketiglooSat, 21 Nov 2009 12:29:47 GMTmilestone changed; failure set
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:20
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:20
<ul>
<li><strong>failure</strong>
set to <em>None/Unknown</em>
</li>
<li><strong>milestone</strong>
changed from <em>6.12.1</em> to <em>6.12 branch</em>
</li>
</ul>
TicketiglooFri, 30 Apr 2010 16:39:16 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:21
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:21
<ul>
<li><strong>milestone</strong>
changed from <em>6.12 branch</em> to <em>6.12.3</em>
</li>
</ul>
TicketiglooSat, 19 Jun 2010 16:56:50 GMTpriority, milestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:22
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:22
<ul>
<li><strong>priority</strong>
changed from <em>normal</em> to <em>low</em>
</li>
<li><strong>milestone</strong>
changed from <em>6.12.3</em> to <em>6.14.1</em>
</li>
</ul>
TicketiglooSun, 19 Dec 2010 17:45:56 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:23
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:23
<ul>
<li><strong>milestone</strong>
changed from <em>7.0.1</em> to <em>7.0.2</em>
</li>
</ul>
TicketiglooSat, 05 Mar 2011 13:16:09 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:24
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:24
<ul>
<li><strong>milestone</strong>
changed from <em>7.0.2</em> to <em>7.2.1</em>
</li>
</ul>
TicketiglooSat, 01 Oct 2011 23:16:14 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:25
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:25
<ul>
<li><strong>milestone</strong>
changed from <em>7.2.1</em> to <em>7.4.1</em>
</li>
</ul>
TicketiglooThu, 09 Feb 2012 15:03:05 GMTpriority, milestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:26
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:26
<ul>
<li><strong>priority</strong>
changed from <em>low</em> to <em>normal</em>
</li>
<li><strong>milestone</strong>
changed from <em>7.4.1</em> to <em>7.6.1</em>
</li>
</ul>
TicketiglooWed, 12 Sep 2012 11:11:57 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:27
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:27
<ul>
<li><strong>milestone</strong>
changed from <em>7.6.1</em> to <em>7.6.2</em>
</li>
</ul>
TicketmorabbinWed, 23 Jan 2013 01:21:18 GMT
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:28
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:28
<p>
Any progress on adapting Doaitse's work for inclusion?
</p>
TicketDoaitseWed, 23 Jan 2013 16:21:50 GMTfailure changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:29
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:29
<ul>
<li><strong>failure</strong>
changed from <em>None/Unknown</em> to <em>Other</em>
</li>
</ul>
<p>
When checking I noticed that the ChristmasTree package had problems compiling with the newer versions of GHC. I updated the package by adding some type annotations, and upgrading the cabal file.
</p>
<p>
The fact that we did not get complaints thus far probably indicates that the problem is not perceived as a severe one by most users. I have no idea how many actually rely on this package or this technology.
</p>
<p>
It would be nice if hackage would inform maintains about packages becoming obsolete due to upgrades of GHC or of packages it depends on, but that is another issue.
</p>
TicketthoughtpoliceMon, 14 Jul 2014 13:04:47 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:30
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:30
<ul>
<li><strong>milestone</strong>
changed from <em>7.6.2</em> to <em>7.10.1</em>
</li>
</ul>
<p>
Moving to 7.10.1.
</p>
TicketthoughtpoliceTue, 23 Dec 2014 13:34:10 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:31
http://ghc.haskell.org/trac/ghc/ticket/1544#comment:31
<ul>
<li><strong>milestone</strong>
changed from <em>7.10.1</em> to <em>7.12.1</em>
</li>
</ul>
<p>
Moving to 7.12.1 milestone; if you feel this is an error and should be addressed sooner, please move it back to the 7.10.1 milestone.
</p>
Ticket