Closed Type Families: separate instance groups?

AntC anthony_clayden at clear.net.nz
Thu Jun 4 22:39:25 UTC 2015


> Simon Peyton Jones <simonpj <at> microsoft.com> writes:
> 
> I think it's pretty good as-is.
> 
Thank you Simon, I'm agreeing with "pretty good",
though possibly not with "pretty" ;-)

> ...
> * Use a closed family (with overlap and top-to-bottom matching)
>    to deal with that part of the space:
> 
> Doing this was a HUGE improvement, ...

(I'm not quite getting improvement over what?
 This was and is the only way to do overlaps with Type Families?)

I'm not ever-so sure I'm seeing an improvement
over overlapping class instances with FunDeps.
I really really want type families to be an improvement
because type manipulation in a functional language
should be -- errm -- functional.

> |  It also BTW cuts us off from using Closed Families as Associated types
> |  separated into their Class instances.
> 
> I don't understand this comment. ...

I'll answer Richard's strapline at
https://typesandkinds.wordpress.com/
"Who needs terms, anyway?":
I need both types and terms.

Yes the compiler needs first a type-solving phase
before dealing with the terms.
Type Families cleanly separate that off.

And in a significant proportion of use cases,
the type-handling is the same across many class instances.
So it's more succinct to collapse the type instances
into a grouped type family ... where ...

There's other use cases for overlapping
where you can't collapse the type-handling.
So then I'm finding that my class instances
have heads that repeat the type instance heads.
And I would use Assoc types
but the type family instances
have to appear in the family, to sequence the top-to-bottom matching.

> Can you give an example that the current setup does not handle?
> 
(This is about dealing with many instances,
 so difficult to give a succinct example.
 And the current setup does handle it OK.
 It's just that it seems verbose, with hard to read code,
 compared to FunDeps. I appreciate that's in the eye of the beholder.)

Take the standard example for partial overlaps.
Suppose I have a class:

    class C a where f :: a -> F a

    instance C (Foo Int c) where
       -- I'd like to put
      type F (Foo Int b) = Int   -- but it overlaps
      f (Foo x _) = x

    instance C (Foo b Char) where
      type F (Foo b Char) = Char -- non-confluent
      f (Foo _ y) = y

Imagine there's dozens of overlapping instances.
(And BTW there's no actual ambiguous usages.
 By construction (Foo Int b) means b /~ Char.
 But I have no way to declare that.)

I'm also getting (in more complex examples)
GHC complaining it can't infer the types
for the result of f.
So now I'm having to put type equality
constraints on the class instances,
to assure it that F comes up with 
the right type.
This just seems easier if I have the result type
as an extra param to the class,
with a FunDep in the classic style:

    class C a b | a -> b where
      f :: a -> b    

(I can supply those more complex examples if need be,
 but this post is already too long.)

AntC





More information about the Glasgow-haskell-users mailing list