Difference between revisions of "GHC/SuperClass"

From HaskellWiki
< GHC
Jump to navigation Jump to search
(dump text from trac #13657)
 
m (change formatting, add links to User Guide)
Line 1: Line 1:
 
[in draft May 2017]
 
[in draft May 2017]
   
There's an idiom that's briefly mentioned under Equality constraints https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints, but is more general and more powerful.
+
There's an idiom that's briefly mentioned in the User Guide under Equality constraints [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints], but is more general and more powerful.
   
 
You get the power from a combination of extensions:
 
You get the power from a combination of extensions:
* -XMultiParamTypeClasses [10.8.1.1]
+
* -XMultiParamTypeClasses [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#multi-parameter-type-classes]
* Superclass constraints, which can also be MultiParam [10.8.1.2]
+
* Superclass constraints, which can also be MultiParam [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#the-superclasses-of-a-class-declaration]
* -XFunctionalDependencies [10.8.2]
+
* -XFunctionalDependencies [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#functional-dependencies]
* and/or superclass Equality constraints with Type families, having the effect of FunDeps https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints
+
* and/or superclass Equality constraints with Type families, having the effect of FunDeps [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints]
* The overall effect can satisfy the FunDep Coverage conditions, which means you might not need -XUndecidableInstances [10.8.3.5]
+
* The overall effect can satisfy the FunDep Coverage conditions, which means you might not need -XUndecidableInstances [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#undecidable-instances]
   
  +
The User Guide [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints] says:
10.14.1 says:
 
  +
<blockquote>
> Equality constraints can also appear in class and instance contexts. The former enable a simple translation of programs using functional dependencies into programs using family synonyms instead.
+
Equality constraints can also appear in class and instance contexts. The former enable a simple translation of programs using functional dependencies into programs using family synonyms instead.
  +
</blockquote>
   
 
That's not wrong; but by talking about "class and instance contexts" in the same breath does fail to emphasise that:
 
That's not wrong; but by talking about "class and instance contexts" in the same breath does fail to emphasise that:
* whereas Instance constraints don't apply until after the instance has been selected\\(And it's a common newbie mistake to think they restrict the selection.)
+
* whereas Instance constraints don't apply until after the instance has been selected<br>(And it's a common newbie mistake to think they restrict the selection.)
* superclass constraints apply during type improvement __before__ selecting instances\\as described here http://stackoverflow.com/questions/35252562/find-an-element-in-an-hlist/35260970#35260970 "superclass context is critical ..." [thanks @DFeuer]
+
* superclass constraints apply during type improvement ''before'' selecting instances<br>as described here [http://stackoverflow.com/questions/35252562/find-an-element-in-an-hlist/35260970#35260970] "superclass context is critical ..." [thanks @DFeuer]
* This behaviour means that even if you don't explicitly put a FunDep `| ... -> ...` on a class:
+
* This behaviour means that even if you don't explicitly put a FunDep <Hask>| ... -> ...</Hask> on a class:
* If you put a superclass equality constraint on a class, that induces a FunDep as explained in 10.14.1; but just as much
+
** If you put a superclass equality constraint on a class, that induces a FunDep as explained in [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints];<br> but just as much
* If you put a superclass __class__ constraint on a class, and that superclass has a FunDep, that also induces a FunDep.
+
** If you put a superclass ''class'' constraint on a class, and that superclass has a FunDep, that also induces a FunDep.
* and so on for a superclass constraint of a superclass constraint of a superclass constraint ....
+
** and so on for a superclass constraint of a superclass constraint of a superclass constraint ....
   
   
I suggest the bulk of the write-up goes under a new sub-section for FunDeps:
 
   
 
== Functional dependency-like behaviour through superclass constraints ==
----
 
 
== 10.8.2.3 Functional dependency-like behaviour through superclass constraints
 
   
 
Constraints in the class context can achieve the effect of Functional dependencies even without the explicit vertical bar and dependency arrow syntax in the class declaration. Either:
 
Constraints in the class context can achieve the effect of Functional dependencies even without the explicit vertical bar and dependency arrow syntax in the class declaration. Either:
   
* Using an Equality constraint to a Type family [10.14.1]; or
+
* Using an Equality constraint to a Type family [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints]; or
 
* A superclass constraint that does have an explicit Functional dependency.
 
* A superclass constraint that does have an explicit Functional dependency.
 
* Or a superclass constraint which is itself constrained in one of those ways.
 
* Or a superclass constraint which is itself constrained in one of those ways.
   
 
A class declaration of the form
 
A class declaration of the form
  +
<Haskell>
{{{
 
 
class C a b | a -> b
 
class C a b | a -> b
  +
</Haskell>
}}}
 
 
can equivalently be given as
 
can equivalently be given as
  +
{{{
 
  +
<Haskell>
 
class (F a ~ b) => C a b where
 
class (F a ~ b) => C a b where
 
type F a
 
type F a
  +
</Haskell>
}}}
 
That is, we represent every functional dependency (FD) `a1 .. an -> b` by an FD type family `F a1 .. an` and a superclass context equality `F a1 .. an ~ b`, essentially giving a name to the functional dependency. [See 10.14.1; using `(~)` needs -XTypeFamilies or -XGADTs.] In class instances, we define the type instances of FD families in accordance with the class head.
+
That is, we represent every functional dependency (FD) <Hask>a1 .. an -> b</Hask> by an FD type family <Hask>F a1 .. an</Hask> and a superclass context equality <Hask>F a1 .. an ~ b</Hask>, essentially giving a name to the functional dependency. [See [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#equality-constraints]; using <Hask>(~)</Hask> needs -XTypeFamilies or -XGADTs.] In class instances, we define the type instances of FD families in accordance with the class head.
   
Or class `C` can equivalently be given as
+
Or class <Hask>C</Hask> can equivalently be given as
  +
{{{
 
  +
<Haskell>
 
class (D a b) => C a b ...
 
class (D a b) => C a b ...
   
Line 53: Line 54:
 
class (G a ~ b) => D a b where
 
class (G a ~ b) => D a b where
 
type G a
 
type G a
  +
</Haskell>
}}}
 
  +
The class instances for `D` will closely follow those for `C`. So there is not much gain in expressivity. Consider also:
+
The class instances for <Hask>D</Hask> will closely follow those for <Hask>C</Hask>. So there is not much gain in expressivity. Consider also:
{{{
 
  +
  +
<Haskell>
 
class (D a c) => E a b c ...
 
class (D a c) => E a b c ...
   
 
-- class D as above
 
-- class D as above
  +
</Haskell>
}}}
 
  +
for which the `D` instances will be more compact than `E`.
+
for which the <Hask>D</Hask> instances will be more compact than <Hask>E</Hask>.
   
Note that `D` might not declare any methods, but merely be used for type improvement.
+
Note that <Hask>D</Hask> might not declare any methods, but merely be used for type improvement.
   
Method signatures for class `C` are not affected by either of these ways for achieving dependencies.
+
Method signatures for class <Hask>C</Hask> are not affected by either of these ways for achieving dependencies.
   
----
 
   
Reference this thread: https://mail.haskell.org/pipermail/glasgow-haskell-users/2017-April/026515.html, with some fancier examples of pseudo-FunDeps.
+
Reference this thread: [https://mail.haskell.org/pipermail/glasgow-haskell-users/2017-April/026515.html], with some fancier examples of pseudo-FunDeps.
   
See also #10431.
 
   
Is this power made clear in the theoretical literature? It's kinda implicit in the TypeFamilies2008 paper; but note that was written concurrently with Type Families development; and published before the work was completed. Specifically, supporting Type Families and `(~)` in superclass constraints was delivered a long time after the rest of the work.
+
Is this power made clear in the theoretical literature? It's kinda implicit in the TypeFamilies2008 paper; but note that was written concurrently with Type Families development; and published before the work was completed. Specifically, supporting Type Families and <Hask>(~)</Hask> in superclass constraints was delivered a long time after the rest of the work.

Revision as of 01:34, 30 May 2017

[in draft May 2017]

There's an idiom that's briefly mentioned in the User Guide under Equality constraints [1], but is more general and more powerful.

You get the power from a combination of extensions:

  • -XMultiParamTypeClasses [2]
  • Superclass constraints, which can also be MultiParam [3]
  • -XFunctionalDependencies [4]
  • and/or superclass Equality constraints with Type families, having the effect of FunDeps [5]
  • The overall effect can satisfy the FunDep Coverage conditions, which means you might not need -XUndecidableInstances [6]

The User Guide [7] says:

Equality constraints can also appear in class and instance contexts. The former enable a simple translation of programs using functional dependencies into programs using family synonyms instead.

That's not wrong; but by talking about "class and instance contexts" in the same breath does fail to emphasise that:

  • whereas Instance constraints don't apply until after the instance has been selected
    (And it's a common newbie mistake to think they restrict the selection.)
  • superclass constraints apply during type improvement before selecting instances
    as described here [8] "superclass context is critical ..." [thanks @DFeuer]
  • This behaviour means that even if you don't explicitly put a FunDep | ... -> ... on a class:
    • If you put a superclass equality constraint on a class, that induces a FunDep as explained in [9];
      but just as much
    • If you put a superclass class constraint on a class, and that superclass has a FunDep, that also induces a FunDep.
    • and so on for a superclass constraint of a superclass constraint of a superclass constraint ....


Functional dependency-like behaviour through superclass constraints

Constraints in the class context can achieve the effect of Functional dependencies even without the explicit vertical bar and dependency arrow syntax in the class declaration. Either:

  • Using an Equality constraint to a Type family [10]; or
  • A superclass constraint that does have an explicit Functional dependency.
  • Or a superclass constraint which is itself constrained in one of those ways.

A class declaration of the form

class C a b | a -> b

can equivalently be given as

class (F a ~ b) => C a b where
  type F a

That is, we represent every functional dependency (FD) a1 .. an -> b by an FD type family F a1 .. an and a superclass context equality F a1 .. an ~ b, essentially giving a name to the functional dependency. [See [11]; using (~) needs -XTypeFamilies or -XGADTs.] In class instances, we define the type instances of FD families in accordance with the class head.

Or class C can equivalently be given as

class (D a b) => C a b ...

class D a b | a -> b
-- or
class (G a ~ b) => D a b where
  type G a

The class instances for D will closely follow those for C. So there is not much gain in expressivity. Consider also:

class (D a c) => E a b c ...

-- class D as above

for which the D instances will be more compact than E.

Note that D might not declare any methods, but merely be used for type improvement.

Method signatures for class C are not affected by either of these ways for achieving dependencies.


Reference this thread: [12], with some fancier examples of pseudo-FunDeps.


Is this power made clear in the theoretical literature? It's kinda implicit in the TypeFamilies2008 paper; but note that was written concurrently with Type Families development; and published before the work was completed. Specifically, supporting Type Families and (~) in superclass constraints was delivered a long time after the rest of the work.