2011/7/23 Gábor Lehel <span dir="ltr">&lt;<a href="mailto:illissius@gmail.com">illissius@gmail.com</a>&gt;</span><br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
2011/7/22 Dan Doel &lt;<a href="mailto:dan.doel@gmail.com">dan.doel@gmail.com</a>&gt;:<br>
<div class="im">&gt; 2011/7/22 Gábor Lehel &lt;<a href="mailto:illissius@gmail.com">illissius@gmail.com</a>&gt;:<br>
&gt;&gt; Yeah, this is pretty much what I ended up doing. As I said, I don&#39;t<br>
&gt;&gt; think I lose anything in expressiveness by going the MPTC route, I<br>
&gt;&gt; just think the two separate but linked classes way reads better. So<br>
&gt;&gt; it&#39;s just a &quot;would be nice&quot; thing. Do recursive equality superclasses<br>
&gt;&gt; make sense / would they be within the realm of the possible to<br>
&gt;&gt; implement?<br>
&gt;<br>
&gt; Those equality superclasses are not recursive in the same way, as far<br>
&gt; as I can tell. The specifications for classes require that there is no<br>
&gt; chain:<br>
&gt;<br>
&gt;    C ... =&gt; D ... =&gt; E ... =&gt; ... =&gt; C ...<br>
&gt;<br>
&gt; However, your example just had (~) as a context for C, but C is not<br>
&gt; required by (~). And the families involved make no reference to C,<br>
&gt; either. A fully desugared version looks like:<br>
&gt;<br>
&gt;    type family Frozen a :: *<br>
&gt;    type family Thawed a :: *<br>
&gt;<br>
&gt;    class (..., Thawed (Frozen t) ~ t) =&gt; Mutable t where ...<br>
&gt;<br>
&gt; I think this will be handled if you use a version where equality<br>
&gt; superclasses are allowed.<br>
<br>
</div>To be completely explicit, I had:<br>
<br>
class (Immutable (Frozen t), Thawed (Frozen t) ~ t) =&gt; Mutable t where<br>
type Frozen t ...<br>
class (Mutable (Thawed t), Frozen (Thawed t) ~ t) =&gt; Immutable t where<br>
type Thawed t ...<br></blockquote><div><br></div><div> </div><div>I had a similar issue in my representable-tries package.</div><div><br></div><div>In there I had</div><div><br></div><div>type family Key (f :: * -&gt; *) :: * </div>
<div><br></div><div>class Indexable f where</div><div>  index :: f a -&gt; Key f -&gt; a</div><div><br></div><div>class Indexable f =&gt; Representable f where</div><div>  tabulate :: (Key f -&gt; a) -&gt; f a</div><div><br>
</div><div>such that tabulate and index witness the isomorphism from f a to (Key f -&gt; a).</div><div><br></div><div>So far no problem. But then to provide a Trie type that was transparent I wanted.</div><div><br></div><div>
class (Representable (BaseTrie e), Traversable (BaseTrie e), Key (BaseTrie e) ~ e) =&gt; HasTrie e where</div><div>  type BaseTrie e :: * -&gt; *</div><div><br></div><div>type (:-&gt;:) e = BaseTrie e </div><div><br></div>
<div>which I couldn&#39;t use prior to the new class constraints patch.</div><div><br></div><div>The reason I mention this is that my work around was to weaken matters a bit</div><div><br></div><div>class (Representable (BaseTrie e)) =&gt; HasTrie e where</div>
<div>  type BaseTrie e :: * -&gt; *</div><div>  embedKey :: e -&gt; Key (BaseTrie e)</div><div>  projectKey :: Key (BaseTrie e) -&gt; e</div><div><br></div><div><div><br class="Apple-interchange-newline">This dodged the need for superclass equality constraints by just requiring me to supply the two witnesses to the isomorphism between e and Key (BaseTrie e).</div>
</div><div><br></div><div><div>Moreover, in my case it helped me produce instances, because the actual signatures involved about 20 more superclasses, and this way I could make new HasTrie instances for newtype wrappers just by defining an embedding and projection pair for some type I&#39;d already defined.</div>
</div><div><br></div><div>But, it did require me to pay for a newtype wrapper which managed the embedding and projection pairs.</div><div><br></div><div><div><div>newtype e :-&gt;: a = Trie (BaseTrie e a)</div></div></div>
<div><br></div><div>In your setting, perhaps something like:</div><div><div style="text-align: left;"> </div>type family Frozen t <br><div><div style="text-align: left; ">type family Thawed t</div></div><div><br></div>class Immutable (Frozen t) =&gt; Mutable t where</div>
<div>  thawedFrozen :: t -&gt; Thawed (Frozen t)</div><div>  unthawedFrozen :: Thawed (Frozen t) -&gt; t <br><br></div><div>class Mutable (Thawed t) =&gt; Immutable t where</div><div>  frozenThawed :: t -&gt; Frozen (Thawed t)</div>
<div>  unfrozenThawed :: Frozen (Thawed t) -&gt; t<br><div style="text-align: left;"><br></div></div><div style="text-align: left;">would enable you to explicitly program with the two isomorphisms, while avoiding superclass constraints.</div>
<div style="text-align: left;"><br></div><div style="text-align: left;">-Edward</div></div>