Magic classes for Overloaded Record Fields, overlaps, FunDeps

Adam Gundry adam at well-typed.com
Tue Apr 26 12:24:58 UTC 2016


Hi AntC,

On 26/04/16 09:20, AntC wrote:
> There's an intriguing comment here wrt anonymous records:
> https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/
> MagicClasses#Designextension:anonymousrecords
> (End of the section)
> 
> "... this doesn't quite work, because the two instances overlap,
>  but it is possible with a bit more trickery"
> 
> I could well understand if everybody's forgotten what was the "trickery", 
> because ORF has been so long in the pipeline, but could anyone explain?

I'm afraid the sentence on the wiki page is slightly misleading, as it
dates from the type families version of the HasField proposal, rather
than the functional dependencies version, and the page hasn't been
updated properly. In fact, with the change to functional dependencies,
the overlapping instances solution works rather nicely, in that it works
if the field labels are distinct and reports an error if not [1]. I'll
update the page.

I have been vacillating between type families and fundeps for the ORF
classes. I hadn't fully appreciated this point about overlap, but I
think it is a reason to prefer fundeps, which is the direction in which
I'm leaning. I'd be grateful for feedback on this issue though!


> Reason for the q: I'm looking at anonymous records myself,
>  including extending, shortening and joining anon records. 
> And yes overlapping instances are everywhere.
> 
> Using type families isn't being too ergonomic. And I notice ORF has used FunDeps.
> But for me, FunDeps in the HList style is also rather ugly.
> 
> Is there some trickery I'm missing?

In general, to avoid overlapping instances, one trick is to introduce a
closed type family that discriminates between the parameters, along with
an auxiliary class whose instances match on the result of the type
family. For example, rather than

    instance C [Int]
    instance C [a]

one can write

    class IsInt a ~ b => CHelper a b
    instance CHelper Int True
    instance IsInt a ~ False => CHelper a   False

    type family IsInt a where
      IsInt Int = True
      IsInt a   = False

    instance CHelper a (IsInt a) => C [a]

This allows one to write instances using top-to-bottom matching, like
closed type families, provided all the instances are declared together.

Hope this helps,

Adam

P.S. If you have any thoughts on the interaction between ORF and
encodings of anonymous records, I'd be interested to hear them.

[1] https://gist.github.com/adamgundry/7292df8cef62fd6750885be3f5f892e7


-- 
Adam Gundry, Haskell Consultant
Well-Typed LLP, http://www.well-typed.com/


More information about the Glasgow-haskell-users mailing list