Type Families with Class, Type Classes with Family
Alejandro Serrano1 Jurriaan Hage1 Patrick Bahr2
1Utrecht University
2IT University of Copenhagen
Haskell Symposium 2015 Goal
I use type families to simulate type classes
I gain flexibility and expressiveness
Motivation
Type-level programming in Haskell/GHC
I functional dependencies
I type families
I data type promotion, kind polymorphism
I closed type families
2 / 18 Motivation
Type-level programming in Haskell/GHC
I functional dependencies
I type families
I data type promotion, kind polymorphism
I closed type families
Goal
I use type families to simulate type classes
I gain flexibility and expressiveness
2 / 18 Overview
I Simulate type classes using type families (without class methods)
I What can we do with this encoding?
I Proposal: type families with elaboration
3 / 18 class C t1 ... tn IsC :: κ1 → ... → κn → Bool Instead of Bool we use:
data Defined = Yes | No
Type family declaration
type family IsC t1 ... tn :: Defined
Encoding type classes
Simplification (for now): type class = predicate on types
4 / 18 Instead of Bool we use:
data Defined = Yes | No
Type family declaration
type family IsC t1 ... tn :: Defined
Encoding type classes
Simplification (for now): type class = predicate on types
class C t1 ... tn IsC :: κ1 → ... → κn → Bool
4 / 18 Type family declaration
type family IsC t1 ... tn :: Defined
Encoding type classes
Simplification (for now): type class = predicate on types
class C t1 ... tn IsC :: κ1 → ... → κn → Bool Instead of Bool we use:
data Defined = Yes | No
4 / 18 Type family declaration
type family IsC t1 ... tn :: Defined
Encoding type classes
Simplification (for now): type class = predicate on types
class C t1 ... tn IsC :: κ1 → ... → κn → Defined Instead of Bool we use:
data Defined = Yes | No
4 / 18 Encoding type classes
Simplification (for now): type class = predicate on types
class C t1 ... tn IsC :: κ1 → ... → κn → Defined Instead of Bool we use:
data Defined = Yes | No
Type family declaration
type family IsC t1 ... tn :: Defined
4 / 18 type Eq a = IsEq a ∼ Yes
Translation into type family declaration
type family IsEq (a :: ∗) :: Defined
Usage
f :: Eq a ⇒ a → a
Example: Eq
Type class declaration
class Eq a where (≡) :: a → a → Bool
5 / 18 type Eq a = IsEq a ∼ Yes
(≡) :: a → a → Bool
Translation into type family declaration
type family IsEq (a :: ∗) :: Defined
Usage
f :: Eq a ⇒ a → a
Example: Eq
Type class declaration
class Eq a where
5 / 18 type Eq a = IsEq a ∼ Yes
(≡) :: a → a → Bool
Usage
f :: Eq a ⇒ a → a
Example: Eq
Type class declaration
class Eq a where
Translation into type family declaration
type family IsEq (a :: ∗) :: Defined
5 / 18 (≡) :: a → a → Bool
type Eq a = IsEq a ∼ Yes
Example: Eq
Type class declaration
class Eq a where
Translation into type family declaration
type family IsEq (a :: ∗) :: Defined
Usage
f :: Eq a ⇒ a → a
5 / 18 (≡) :: a → a → Bool
type Eq a = IsEq a ∼ Yes
Example: Eq
Type class declaration
class Eq a where
Translation into type family declaration
type family IsEq (a :: ∗) :: Defined
Usage
f :: IsEq a ∼ Yes ⇒ a → a
5 / 18 (≡) :: a → a → Bool
Example: Eq
Type class declaration
class Eq a where
Translation into type family declaration
type family IsEq (a :: ∗) :: Defined
Usage type Eq a = IsEq a ∼ Yes f :: IsEq a ∼ Yes ⇒ a → a
5 / 18 (≡) :: a → a → Bool
Example: Eq
Type class declaration
class Eq a where
Translation into type family declaration
type family IsEq (a :: ∗) :: Defined
Usage type Eq a = IsEq a ∼ Yes f :: Eq a ⇒ a → a
5 / 18 Type family instance declarations
type instance IsEq Int = Yes type instance IsEq [a] = IsEq a type instance IsEq (a, b) = And (IsEq a)(IsEq b)
Example: Eq
Type class instance declarations
instance Eq Int where ... instance Eq a ⇒ Eq [a] where ... instance (Eq a, Eq b) ⇒ Eq (a, b) where ...
6 / 18 Example: Eq
Type class instance declarations
instance Eq Int where ... instance Eq a ⇒ Eq [a] where ... instance (Eq a, Eq b) ⇒ Eq (a, b) where ...
Type family instance declarations
type instance IsEq Int = Yes type instance IsEq [a] = IsEq a type instance IsEq (a, b) = And (IsEq a)(IsEq b)
6 / 18 Example: Eq
Type class instance declarations
instance Eq Int where ... instance Eq a ⇒ Eq [a] where ... instance (Eq a, Eq b) ⇒ Eq (a, b) where ...
Type familytype instance family And declarations(x :: Defined)(y :: Defined) :: Defined where And Yes Yes = Yes type instanceAnd xIsEq y Int= No= Yes type instance IsEq [a] = IsEq a type instance IsEq (a, b) = And (IsEq a)(IsEq b)
6 / 18 What Can We Express
I super classes
I limited forms functional dependencies (requires injective type families)
I not supported: overlapping instances
7 / 18 I type class directives
I custom error messages
I instance chains
What else can we do with this encoding?
8 / 18 What else can we do with this encoding?
I type class directives
I custom error messages
I instance chains
8 / 18 Couldn’t match type ’No with ’Yes Expected type: ’Yes Actual type: IsShow (t -> t)
Non-membership
type instance IsShow (a → b) = No
9 / 18 Non-membership
type instance IsShow (a → b) = No
Couldn’t match type ’No with ’Yes Expected type: ’Yes Actual type: IsShow (t -> t)
9 / 18 Closed set of instances
type family IsIntegral t where IsIntegral Int = Yes IsIntegral Integer = Yes IsIntegral t = No
10 / 18 type instance IsIntegralOrFractional Int = Integral type instance IsIntegralOrFractional Integer = Integral type instance IsIntegralOrFractional Double = Fractional
type IsIntegral t = IsIntegral0 (IsIntegralOrFractional t) type family IsIntegral0 c :: Defined where IsIntegral0 Integral = Yes IsIntegral0 c = No
Disjointness
data IntegralOrFractional = Integral | Fractional | None
type family IsIntegralOrFractional t :: IntegralOrFractional
11 / 18 type IsIntegral t = IsIntegral0 (IsIntegralOrFractional t) type family IsIntegral0 c :: Defined where IsIntegral0 Integral = Yes IsIntegral0 c = No
Disjointness
data IntegralOrFractional = Integral | Fractional | None
type family IsIntegralOrFractional t :: IntegralOrFractional
type instance IsIntegralOrFractional Int = Integral type instance IsIntegralOrFractional Integer = Integral type instance IsIntegralOrFractional Double = Fractional
11 / 18 Disjointness
data IntegralOrFractional = Integral | Fractional | None
type family IsIntegralOrFractional t :: IntegralOrFractional
type instance IsIntegralOrFractional Int = Integral type instance IsIntegralOrFractional Integer = Integral type instance IsIntegralOrFractional Double = Fractional
type IsIntegral t = IsIntegral0 (IsIntegralOrFractional t) type family IsIntegral0 c :: Defined where IsIntegral0 Integral = Yes IsIntegral0 c = No
11 / 18 e e
Example
type IsIntegral t = IsIntegral0 (IsIntegralOrFractional t) type family IsIntegral0 c :: Defined Symbol where IsIntegral0 Integral = Yes IsIntegral0 Fractional = No "is instance of Factional" IsIntegral0 c = No ""
Couldn’t match type ’No "is instance of Fractional" with ’Yes Expected type: ’Yes Actual type: IsIntegral Double
Custom Error Messages
data Defined = Yes | No
12 / 18 Example
type IsIntegral t = IsIntegral0 (IsIntegralOrFractional t) type family IsIntegral0 c :: Defined Symbol where IsIntegral0 Integral = Yes IsIntegral0 Fractional = No "is instance of Factional" IsIntegral0 c = No ""
Couldn’t match type ’No "is instance of Fractional" with ’Yes Expected type: ’Yes Actual type: IsIntegral Double
Custom Error Messages
data Defined e = Yes | No e
12 / 18 Couldn’t match type ’No "is instance of Fractional" with ’Yes Expected type: ’Yes Actual type: IsIntegral Double
Custom Error Messages
data Defined e = Yes | No e
Example
type IsIntegral t = IsIntegral0 (IsIntegralOrFractional t) type family IsIntegral0 c :: Defined Symbol where IsIntegral0 Integral = Yes IsIntegral0 Fractional = No "is instance of Factional" IsIntegral0 c = No ""
12 / 18 Custom Error Messages
data Defined e = Yes | No e
Example
type IsIntegral t = IsIntegral0 (IsIntegralOrFractional t) type family IsIntegral0 c :: Defined Symbol where IsIntegral0 Integral = Yes IsIntegral0 Fractional = No "is instance of Factional" IsIntegral0 c = No ""
Couldn’t match type ’No "is instance of Fractional" with ’Yes Expected type: ’Yes Actual type: IsIntegral Double 12 / 18 Example instance Show (a → b) if (Enum a, Show a, Show b) where show = ... else instance Show (a → b) fails
As type family
type instance IsShow (a → b) = IsShowFn a b type family IsShowFn a b = And3 (IsEnum a)(IsShow a)(IsShow b)
Instance Chains [Morris & Jones]
I explicit failure
I provide alternatives (else clause)
13 / 18 As type family
type instance IsShow (a → b) = IsShowFn a b type family IsShowFn a b = And3 (IsEnum a)(IsShow a)(IsShow b)
Instance Chains [Morris & Jones]
I explicit failure
I provide alternatives (else clause)
Example instance Show (a → b) if (Enum a, Show a, Show b) where show = ... else instance Show (a → b) fails
13 / 18 Instance Chains [Morris & Jones]
I explicit failure
I provide alternatives (else clause)
Example instance Show (a → b) if (Enum a, Show a, Show b) where show = ... else instance Show (a → b) fails
As type family
type instance IsShow (a → b) = IsShowFn a b type family IsShowFn a b = And3 (IsEnum a)(IsShow a)(IsShow b)
13 / 18 Two possibilities:
I Use type classes
I Extend type families with elaboration
What about class methods?
14 / 18 What about class methods?
Two possibilities:
I Use type classes
I Extend type families with elaboration
14 / 18 p p
What about class methods? data Defined e = Yes | No e
Two possibilities:
I Use type classes
I Extend type families with elaboration
14 / 18 What about class methods? data Defined e p = Yes p | No e
Two possibilities:
I Use type classes
I Extend type families with elaboration
14 / 18 What about class methods?
Two possibilities:
I Use type classes
I Extend type families with elaboration
14 / 18 What about class methods?
Two possibilities:
I Use type classes
I Extend type families with elaboration
14 / 18 Example
type family IsEq (t :: ∗) :: Defined dictionary eq :: t → t → Bool
type instance IsEq Int = Yes dictionary eq = primEqInt -- the primitive Int comparison
type instance IsEq [a] = e@(IsEq a) dictionary eq [ ] [ ] = True eq (x : xs)(y : ys) = e@eq x y ∧ eq xs ys eq = False
Elaboration at Rewriting assign a dictionary to type families
15 / 18 type instance IsEq Int = Yes dictionary eq = primEqInt -- the primitive Int comparison
type instance IsEq [a] = e@(IsEq a) dictionary eq [ ] [ ] = True eq (x : xs)(y : ys) = e@eq x y ∧ eq xs ys eq = False
Elaboration at Rewriting assign a dictionary to type families Example
type family IsEq (t :: ∗) :: Defined dictionary eq :: t → t → Bool
15 / 18 type instance IsEq [a] = e@(IsEq a) dictionary eq [ ] [ ] = True eq (x : xs)(y : ys) = e@eq x y ∧ eq xs ys eq = False
Elaboration at Rewriting assign a dictionary to type families Example
type family IsEq (t :: ∗) :: Defined dictionary eq :: t → t → Bool
type instance IsEq Int = Yes dictionary eq = primEqInt -- the primitive Int comparison
15 / 18 Elaboration at Rewriting assign a dictionary to type families Example
type family IsEq (t :: ∗) :: Defined dictionary eq :: t → t → Bool
type instance IsEq Int = Yes dictionary eq = primEqInt -- the primitive Int comparison
type instance IsEq [a] = e@(IsEq a) dictionary eq [ ] [ ] = True eq (x : xs)(y : ys) = e@eq x y ∧ eq xs ys eq = False
15 / 18 Example: Data Types `ala Carte
type family f :<: g :: Defined dictionary inj :: f a → g a where e :<: e = Yes dictionary inj = id f :<:(x :+: y) = d@(Choose f x y l@(f :<: x) r@(f :<: y)) dictionary inj = d@choice l@inj r@inj f :<: g = No
type family Choose f x y fx fy :: Defined dictionary choice :: (f a → x a) → (f a → y a) → f a → (x :+: y) a where Choose f x y Yes fy = Yes dictionary choice x y = Inl ◦ x Choose f x y fx Yes = Yes dictionary choice x y = Inr ◦ y Choose f x y fx fy = No
16 / 18 In the paper
I encoding of functional dependencies
I encoding of superclasses
I more examples
I proof of soundness & completeness (using OutsideIn(X))
Conclusions
Future work
I study elaboration further
I use encoding as implementation of type classes?
17 / 18 Conclusions
Future work
I study elaboration further
I use encoding as implementation of type classes?
In the paper
I encoding of functional dependencies
I encoding of superclasses
I more examples
I proof of soundness & completeness (using OutsideIn(X))
17 / 18 Type Families with Class, Type Classes with Family
Alejandro Serrano1 Jurriaan Hage1 Patrick Bahr2
1Utrecht University
2IT University of Copenhagen
Haskell Symposium 2015