Closed RyanGlScott closed 9 years ago
This strikes me as fairly ad hoc.
Note that e.g.
data Stream a = Stream a (Stream a)
actually does want the direct recursion for its pointed instance.
It is ad hoc, admittedly. I was trying to avoid scenarios like:
data Tree a = Node (Tree a) a (Tree a) | Leaf a
from never returning, and disallowing direct recursion was the most straightforward way. Of course, this won't work for certain things (list and streams being prime examples).
If this is unpalatable, I can easily remove it. Or, if you don't like the idea of generically creating Pointed
instances at all, let me know—you won't hurt my feelings :)
I think I'm going to decline the pull request for now.
It is rather gratuitous and unmotivated and encourages a "bad style" of going "point first" and not thinking about what is the point for your particular operation.
This adds two modules allowing for
Generic
implementations ofPointed
andCopointed
instances. The algorithms for each are as follows:genericPoint
tries to use the leftmost constructor, filling in occurrences of the type parameter with the argument togenericPoint
, and relying on other fields to be instances ofDefault
(for kind*
) orPointed
(for kind* -> *
) for them to be filled in. If a constructor contains direct data recursion (e.g.,data Recur a = Recur (Recur a)
, then this clearly won't terminate, sogenericPoint
will skip such a constructor (if possible).genericCopoint
tries to find the leftmost "hole" from which to pluck an occurrence of the type parameter, checking each constructor from left to right. If a hole contains direct data recursion, that hole is skipped (if possible).These seem like reasonable assumptions to me, but let me know if you'd prefer it be implemented in a different way.