That is a great initiative.<br>I didn&#39;t know about those Kind extensions that enable you to pass a typeclass as a type parameter...<br><br>However, have you considered putting the Data.Exists.Default module in a separate package? That would reduce the dependencies for those who just need Exists and Existential.<br>

<br><div class="gmail_quote">2012/2/5 Gábor Lehel <span dir="ltr">&lt;<a href="mailto:illissius@gmail.com">illissius@gmail.com</a>&gt;</span><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

There&#39;s a common pattern in Haskell of writing:<br>
<br>
data E where E :: C a =&gt; a -&gt; E<br>
also written<br>
data E = forall a. C a =&gt; E a<br>
<br>
I recently uploaded a package to Hackage which uses the new<br>
ConstraintKinds extension to factor this pattern out into an Exists<br>
type parameterized on the constraint, and also for an Existential type<br>
class which can encompass these kind of types:<br>
<br>
<a href="http://hackage.haskell.org/package/exists" target="_blank">http://hackage.haskell.org/package/exists</a><br>
<br>
My motivation was mostly to play with my new toys, if it turns out to<br>
be useful for anything that&#39;s a happy and unexpected bonus.<br>
<br>
Some interesting things I stumbled upon while writing it:<br>
<br>
- Did you know you can write useful existentials for Functor,<br>
Foldable, and Traversable? I sure didn&#39;t beforehand.<br>
<br>
- You can even write them for various Comonad classes, though in their<br>
case I don&#39;t think it&#39;s good for anything because you have no way to<br>
run them.<br>
<br>
- Surprisingly to me, the only * kinded class in the standardish<br>
libraries I found which is useful with existentials is Show, the * -&gt;<br>
* kinded ones are more numerous.<br>
<br>
- I don&#39;t know if anyone&#39;s ever set out what the precise requirements<br>
are for a type class method to be useful with existentials. For<br>
example, any method which requires two arguments of the same type (the<br>
type in the class head) is clearly useless, because if you have two<br>
existentials there&#39;s no way to tell whether or not their contents were<br>
of the same type. I think this holds any time you have more than one<br>
value of the type among the method&#39;s parameters in any kind of way<br>
(even if it&#39;s e.g. a single parameter that&#39;s a list). If the<br>
type-from-the-class-head (is there a word for this?) is used in the<br>
method&#39;s parameters in a position where it&#39;s not the outermost type<br>
constructor of a type (i.e. it&#39;s a type argument), that&#39;s also no<br>
good, because there&#39;s no way to extract the type from the existential,<br>
you can only extract the value. On the other hand, in the method&#39;s<br>
return type it&#39;s fine if there are multiple values of the<br>
type-from-the-class-head (or if it&#39;s used as a type argument?),<br>
because (as long as the method also has an argument of the type) the<br>
type to put into the resulting existentials can be deduced to be the<br>
same as the one that was in the argument. But if the<br>
type-from-the-class-head is used *only* in the return type, then it&#39;s<br>
difficult to construct an existential out of the return value because<br>
the instance to use will be ambiguous.<br>
<br>
- There are a lot of ways you can write existentials, and the library<br>
only captures a small part of them. Multiparameter constraint? No go.<br>
More than one constraint? No go (though you can use<br>
Control.Constraint.Combine). More than one type/value stored? No go.<br>
Anything which doesn&#39;t exactly match the patterns data E where E :: C<br>
a =&gt; a -&gt; E or data E a where E :: C f =&gt; f a -&gt; E a? No go. I don&#39;t<br>
think there&#39;s any way to capture all of the possibilities in a finite<br>
amount of code.<br>
<br>
- ConstraintKinds lets you write class aliases as type synonyms, type<br>
Stringy a = (Show a, Eq a). The old way to do this is class (Show a,<br>
Eq a) =&gt; Stringy a; instance (Show a, Eq a) =&gt; Stringy a and requires<br>
UndecidableInstances. But if the alias has multiple parameters, the<br>
old way is still superior, because it can be partially applied where<br>
type synonyms can&#39;t. This is analogous to the situation with type<br>
synonyms versus newtype/data declarations, but interestingly, unlike<br>
data and newtypes, the class+instance method doesn&#39;t require you to do<br>
any manual wrapping and unwrapping, only the declaration itself is<br>
different.<br>
<br>
- One of the advantages FunctionalDependencies has over TypeFamilies<br>
is that type signatures using them tend to be more readable and<br>
concise than ones which have to write out explicit equality<br>
constraints. For example, foo :: MonadState s m =&gt; s -&gt; m () is nicer<br>
than foo :: (MonadState m, State m ~ s) =&gt; s -&gt; m (). But with<br>
equality superclass constraints (as of GHC 7.2), it&#39;s possible to<br>
translate from TF-form to FD-form (but not the reverse, as far as I<br>
know): class (MonadStateTF m, s ~ State m) =&gt; MonadStateFDish s m;<br>
instance (MonadStateTF m, s ~ State m) =&gt; MonadStateFDish s m.<br>
<br>
- PolyKinds only seems to be useful as long as there&#39;s no value-level<br>
representation of the polykinded type involved (it&#39;s only used as a<br>
phantom). As soon as you have to write &#39;a&#39; for kind * and &#39;f a&#39; for<br>
kind * -&gt; *, you have to do the duplication manually. Is this right?<br>
<br>
- Writing this library really made me want to have a type-level &quot;Ord<br>
instance&quot; for constraints, more precisely a type-level is-implied-by<br>
operator. The typechecker clearly knows that Eq is-implied-by Ord, for<br>
example, and that Foo is-implied-by (Foo :&amp;: Bar), but I have no way<br>
to ask it, I can only use (~). I tried implementing this with<br>
OverlappingInstances, but it seems to be fundamentally impossible<br>
because you really need a transitive case (instance (c :&lt;=: d, d :&lt;=:<br>
e) =&gt; c :&lt;=: e) but the transitive case can&#39;t work. (My best<br>
understanding is that it&#39;s because the typechecker doesn&#39;t work<br>
forward, seeing &quot;ah, c :&lt;=: d and d :&lt;=: e, therefore c :&lt;=: e&quot;;<br>
rather it works backwards, and sees that &quot;c might be :&lt;=: e, if<br>
there&#39;s a suitable d&quot;, but then it has no idea what to choose for d<br>
and goes into a loop.) Filing a feature request is in the plans.<br>
<br>
Er... &lt;/ul&gt;.<br>
<br>
Cheers,<br>
~g<br>
<br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/haskell-cafe" target="_blank">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br>
</blockquote></div><br>