2011/7/23 Gábor Lehel <span dir="ltr"><<a href="mailto:illissius@gmail.com">illissius@gmail.com</a>></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 <<a href="mailto:dan.doel@gmail.com">dan.doel@gmail.com</a>>:<br>
<div class="im">> 2011/7/22 Gábor Lehel <<a href="mailto:illissius@gmail.com">illissius@gmail.com</a>>:<br>
>> Yeah, this is pretty much what I ended up doing. As I said, I don't<br>
>> think I lose anything in expressiveness by going the MPTC route, I<br>
>> just think the two separate but linked classes way reads better. So<br>
>> it's just a "would be nice" thing. Do recursive equality superclasses<br>
>> make sense / would they be within the realm of the possible to<br>
>> implement?<br>
><br>
> Those equality superclasses are not recursive in the same way, as far<br>
> as I can tell. The specifications for classes require that there is no<br>
> chain:<br>
><br>
> C ... => D ... => E ... => ... => C ...<br>
><br>
> However, your example just had (~) as a context for C, but C is not<br>
> required by (~). And the families involved make no reference to C,<br>
> either. A fully desugared version looks like:<br>
><br>
> type family Frozen a :: *<br>
> type family Thawed a :: *<br>
><br>
> class (..., Thawed (Frozen t) ~ t) => Mutable t where ...<br>
><br>
> I think this will be handled if you use a version where equality<br>
> superclasses are allowed.<br>
<br>
</div>To be completely explicit, I had:<br>
<br>
class (Immutable (Frozen t), Thawed (Frozen t) ~ t) => Mutable t where<br>
type Frozen t ...<br>
class (Mutable (Thawed t), Frozen (Thawed t) ~ t) => 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 :: * -> *) :: * </div>
<div><br></div><div>class Indexable f where</div><div> index :: f a -> Key f -> a</div><div><br></div><div>class Indexable f => Representable f where</div><div> tabulate :: (Key f -> a) -> f a</div><div><br>
</div><div>such that tabulate and index witness the isomorphism from f a to (Key f -> 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) => HasTrie e where</div><div> type BaseTrie e :: * -> *</div><div><br></div><div>type (:->:) e = BaseTrie e </div><div><br></div>
<div>which I couldn'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)) => HasTrie e where</div>
<div> type BaseTrie e :: * -> *</div><div> embedKey :: e -> Key (BaseTrie e)</div><div> projectKey :: Key (BaseTrie e) -> 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'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 :->: 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) => Mutable t where</div>
<div> thawedFrozen :: t -> Thawed (Frozen t)</div><div> unthawedFrozen :: Thawed (Frozen t) -> t <br><br></div><div>class Mutable (Thawed t) => Immutable t where</div><div> frozenThawed :: t -> Frozen (Thawed t)</div>
<div> unfrozenThawed :: Frozen (Thawed t) -> 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>