I\'m working on a library where I want to define a recursive class that I\'ve simplified here to:
{-# LANGUAGE MultiParamTypeClasses
, FlexibleIn
The answer to the linked question hides the following quite useful trick: generalize the instance head, and specialize in the instance context.
instance a ~ b => StackTo a Top b where
runStack _ = id
When choosing an instance to use, GHC inspects available instance heads only -- not contexts -- and picks the one (if any) that matches what is currently known about a type. It will not specialize a type before making this choice, even if specializing would allow one or more of the available instance heads to match. Thus, the difference between the instance given here and the one in the question above is that this one is more general: this one applies whenever the middle type is Top
, whereas yours applies only when the middle type is Top
and we know enough about the two other types to know they're equal.
Yours will overlap with fewer other potential instances, but this will encourage the inference engine more strongly.
Is there some particular reason why the Kleene star GADT won't do this job?
data Star r a b where
Nil :: Star r a a
Cons :: r a b -> Star r b c -> Star r a c
compose :: Star (->) a b -> a -> b
compose Nil = id
compose (Cons f fs) = compose fs . f
But if you need a type class approach, I wouldn't interfere.