Is it time to start deprecating FunDeps?

AntC anthony_clayden at clear.net.nz
Tue Apr 30 07:31:09 CEST 2013


Now that the Type Equality coercions extension is stable and has been 
blessed by SPJ as "a functional-dependency-like mechanism (but using 
equalities) for the result type" [in 
http://hackage.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields#Hig
herranktypesandtypefunctions],
no code is compelled to use FunDeps.

[Note that this is orthogonal to the issue of overlaps. You can use the 
equalities mechanism happily with overlaps, see example below.
 This is also orthogonal to type functions/families/associated types. You 
can use the equalities mechanism happily without type families.]

There's plenty of code using FunDeps, so we couldn't just withdraw the 
extension. But we could start deprecating it.

Better still, given that there is a mechanical way to convert FunDeps to 
equalities, we could start treating the FunDep on a class declaration as 
documentation, and validate that the instances observe the mechanical 
translation.

Here's an example of the mechanical translation for HDeleteMany, the 
classic awkward example from HList.

The problem is to delete all occurences of element type e in a HList l. 
Here's the 'non-solution' naieve attempt from the HList paper:

    class HDeleteMany e l l' | e l -> l'
    instance HDeleteMany a HNil HNil       -- base case OK
    instance (HList l, HDeleteMany e l l')
          => HDeleteMany e (HCons e l) l'  -- element match, so omit
    instance (HList l, HDeleteMany e l l')
          => HDeleteMany e (HCons e' l) (HCons e' l')
                                           -- element not match, so retain
                                           -- + recurse on the tail

"The two overlapping instance heads for HCons are in no substitution 
ordering."

Here's the mechanical translation, which _does_ compile:

    class HDeleteMany e l l'                -- | e l -> l'
    instance (HNil ~ l')                    -- force the result
          => HDeleteMany a HNil l'          -- base case OK
    instance (HList l, HDeleteMany e l l')
          => HDeleteMany e (HCons e l) l'   -- same as above
    instance (HList l, HDeleteMany e l l'', (HCons e' l'') ~ l')
          => HDeleteMany e (HCons e' l) l'  -- force the result

The translation rules (applying for the 'target' term of a FunDep) are:
1. If the target is a bare typevar not appearing otherwise in the head,
   we're done OK.
Otherwise:
2. Replace the target with a fresh typevar.
   (This forces instance match without inspecting the use site.)
3. Add a type equality constraint
   equating the fresh typevar with the as-was target term.
   (This forces the result type, in the same way as a FunDep target.)


Possible GSOC project?






More information about the Haskell-prime mailing list