Search code examples
haskellfunctional-dependenciestype-families

Converting Functional Dependency class to Type Family instances


Is it possible to create type family instances from a fundep class? For example, let's say that I have the class

class A a b | a -> b

with some instances (imported an external library) and want to create all corresponding instances for the type family

type family A' a :: *

such that A' a ~ b iff A a b, without having to manually copy and modify the instances from the external source.

How would I do that (if it is possible)?


My most promising attempt so far,

class A' a where
    type A'_b a :: *

instance forall a b. A a b => A' a where
    type A'_b a = b

gives the error message

    The RHS of an associated type declaration mentions ‘b’
      All such variables must be bound on the LHS

So, my guess is that the answer is no? :/


Solution

  • Don't overthink it.

    class C a b | a -> b
    instance C T U
    

    roughly translates into

    class C' a where
        type B a
    instance C' T where
        type B T = U
    

    They're semantically somewhat different, even though they behave in a similar way. C is a two-parameter class, but its second parameter is uniquely determined by its first. C' is a one-parameter class with an associated type. Associated types are interpreted as top-level axioms for FC's coercion system whereas fundeps basically just affect unification.

    It is possible to convert between the two styles, but you have to use a newtype to bind the variables in the type equation.

    newtype WrappedB a = WrappedB { unwrapB :: B a }
    instance C' a => C a (WrappedB a)  -- you can't use a type synonym family in an instance
    
    newtype Wrap a b = Wrap { unWrap :: a }
    instance C a b => C' (Wrap a b) where
        type B (Wrap a b) = b  -- b needs to be bound on the LHS of this equation
    

    In general I don't advise writing the sort of generic instance you've attempted in your question, because such instances have a tendency to be overlapping.

    Functional dependencies are less, um, weird than type families, though. All other things being equal I tend to prefer a fundep.