<<

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 promotion, 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

IsC t1 ... tn :: Defined

Encoding type classes

Simplification (for now): = 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 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