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?
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.
For part 4: the Haskell Report says:
Assume that the type variables in the instance type
(T u1 … uk)
satisfy the constraints in the instance contextcx′
. Under this assumption, the following two conditions must also be satisfied:
- The constraints expressed by the superclass context
cx[(T u1 … uk)/u]
ofC
must be satisfied. In other words,T
must be an instance of each ofC
's superclasses and the contexts of all superclass instances must be implied bycx′
.- Any constraints on the type variables in the instance type that are required for the class method declarations in
d
to be well-typed must also be satisfied.
None of this discussion assumes that superclass contexts are of the simplified form demanded elsewhere in the report; and therefore can (and does) serve as the meaning of more complicated superclass contexts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With