Search code examples
haskellghctypeclassfrptype-families

Which language extensions enable to write "class A (B c) => D c where ..." ? What is the meaning of this type class declaration?


I am trying to understand the following class declaration :

class (MonadHold t (PushM t), MonadSample t (PullM t), Functor (Event t), Functor (Behavior t)) => Reflex t where
  data Behavior t :: * -> *

which is in the Reflex FRP library.

1 ) What language extensions make it possible to write Functor (Behavior t) => Reflex t ? I guess it is a combination of Language extensions. I am guessing Type Families are involved, but what else ?

In other words, what language extension do I have to turn on such that a code containing class A (B c) => D c where ... compiles ?

2 ) What is the meaning of class A (B c) => D c where ... ?

3 ) Could you give a super simple example explaining why and when one wants to write class A (B c) => D c where ... ?

4 ) Where is it described what class A (B c) => D c where ... means ? I guess there is an SPJ paper or talk describing it could you please point to it ?

Edit:

Further related info:

Here it is written:

In Haskell 98 the context of a class declaration (which introduces superclasses) must be simple; that is, each predicate must consist of a class applied to type variables.

If I interpret the above citation correctly then in Haskell98 only class A b=>C b where shaped class declarations are allowed, which means that class A(B c) => D c is not allowed. So the question is, if the latter is allowed, and the former is not, who and what defines the MEANING for the latter ? The latter is not correct Haskell 98 syntax so its meaning is also not described in any Haskell 98 book, then where it is the MEANING of the latter described/documented/specified?


Solution

  • First of all, as you've correctly guessed, Behaviour is an associated data type, so falls under the TypeFamilies extension.

    The point of class Functor (Behaviour t) => Reflex t where data Behaviour t :: * -> * is to enforce the requirement that for any type t which is an instance of Reflex, whatever datatype you define as the associated data type Behaviour t, you must also make that type into an instance of Functor.

    Here's a small example: suppose I this class definition:

    {-# LANGUAGE TypeFamilies, FlexibleContexts #-}
    
    class Functor (F a) => Funky a where
        data F a :: * -> *
    

    This allows writing something like

    frobulate :: (Funky a) => F a Int -> F a Bool
    frobulate = fmap (< 5)
    

    since frobulate is free to assume from Funky a that Functor (F a).

    The following by itself is rejected by the type checker:

    instance Funky Int where
        data F Int a = MkF a
    

    because the associated datatype F Int is not an instance of Functor:

    No instance for (Functor (F Int))
      arising from the superclasses of an instance declaration
    In the instance declaration for `Funky Int'
    

    which forces you to also add an instance definition like

    instance Functor (F Int) where
        fmap f (MkF x) = MkF (f x)
    

    Note that the latter Functor instance also requires FlexibleInstances to be turned on.