Typecase

A

Design

Pattern

For

Type-Indexed

Functions

A

type-indexed

function

is

a

function

that

is

defined

for

each

member

of

some

family

of

types

Haskell

's

type

class

mechanism

provides

collections

of

open

type-indexed

functions

in

which

the

indexing

family

can

be

extended

by

defining

a

new

type

class

instance

but

the

collection

of

functions

is

fixed

The

purpose

of

this

paper

is

to

present

TypeCase

a

design

pattern

that

allows

the

definition

of

closed

type-indexed

functions

in

which

the

index

family

is

fixed

but

the

collection

of

functions

is

extensible

It

is

inspired

by

Cheney

and

Hinze

's

work

on

lightweight

approaches

to

generic

programming

We

generalise

their

techniques

as

a

design

pattern

Furthermore

we

show

that

type-indexed

functions

with

type-indexed

types

and

consequently

generic

functions

with

generic

types

can

also

be

encoded

in

a

lightweight

manner

thereby

overcoming

one

of

the

main

limitations

of

the

lightweight

approaches

D.

-LSB-

Programming

Languages

-RSB-

Language

Constructs

and

Features

Languages

A

type-indexed

function

is

a

function

that

is

defined

for

each

member

of

a

family

of

types

One

of

the

most

popular

mechanisms

implementing

this

notion

is

the

Haskell

-LSB-

-RSB-

type

class

system

A

type

class

consists

of

a

collection

of

related

type-indexed

functions

the

family

of

index

types

is

the

set

of

instances

of

the

type

class

Type

classes

provide

just

one

possible

interpretation

of

the

notion

of

type-indexed

functions

In

particular

they

assume

an

open-world

perspective

the

family

of

index

types

is

extensible

by

defining

a

new

type

class

instance

for

that

type

but

the

collection

of

type-indexed

functions

is

fixed

in

the

type

class

interface

so

needs

to

be

known

in

advance

For

some

applications

particularly

when

providing

a

framework

for

generic

programming

the

family

of

index

types

is

fixed

-LRB-

albeit

large

-RRB-

and

the

collection

of

type-indexed

functions

is

not

known

in

advance

so

a

closed-world

perspective

would

make

more

sense

Permission

to

make

digital

or

hard

copies

of

all

or

part

of

this

work

for

personal

or

classroom

use

is

granted

without

fee

provided

that

copies

are

not

made

or

distributed

for

profit

or

commercial

advantage

and

that

copies

bear

this

notice

and

the

full

citation

on

the

first

page

To

copy

otherwise

to

republish

to

post

on

servers

or

to

redistribute

to

lists

requires

prior

specific

permission

and/or

a

fee

Haskell

September

Tallinn

Estonia

Copyright

c

ACM

The

original

concept

of

a

design

pattern

has

its

origins

in

Christopher

Alexander

's

work

in

architecture

but

it

has

been

picked

up

with

enthusiasm

by

the

object-oriented

programming

community

The

idea

of

design

patterns

is

to

capture

abstract

and

record

beneficial

recurring

patterns

in

software

design

Sometimes

those

patterns

can

be

captured

formally

as

programming

language

constructs

or

software

library

fragments

Often

however

the

appropriate

abstraction

can

not

be

directly

stated

either

because

of

a

lack

of

expressiveness

in

the

language

or

because

there

is

inherent

ambiguity

in

the

pattern

Alexander

describes

a

pattern

as

a

solution

you

can

use

-LSB-

-RSB-

a

million

times

over

without

ever

doing

it

the

same

way

twice

-LSB-

-RSB-

In

this

case

one

must

resort

to

an

informal

description

Even

if

the

abstraction

itself

can

be

captured

formally

one

might

argue

that

a

complete

description

of

the

pattern

includes

necessarily

informal

information

a

name

motivation

examples

consequences

implementation

trade-offs

and

so

on

In

this

paper

we

present

a

technique

that

allows

the

definition

of

closed

type-indexed

functions

as

opposed

to

the

open

type-indexed

functions

provided

by

type

classes

we

do

so

in

the

format

of

a

design

pattern

Our

inspiration

comes

from

previous

research

on

lightweight

approaches

to

generic

programming

-LRB-

LAGP

-RRB-

In

particular

Hinze

's

two

papers

A

Lightweight

Implementation

of

Generics

and

Dynamics

-LSB-

-RSB-

-LRB-

LIGD

with

James

Cheney

-RRB-

and

Generics

for

the

Masses

-LSB-

-RSB-

-LRB-

GM

-RRB-

provide

our

motivation

and

basis

Those

two

papers

focus

on

the

particular

context

of

generic

programming

and

provide

a

number

of

techniques

that

can

be

used

to

encode

first-class

generic

functions

in

Haskell

However

those

techniques

have

a

wider

applicability

not

addressed

by

Hinze

We

propose

a

generalisation

of

the

technique

and

demonstrate

its

use

in

a

variety

of

applications

Our

specific

contributions

are

Generalisation

of

the

lightweight

approaches

We

provide

templates

for

designing

closed

type-indexed

functions

abstracting

away

from

generic

programming

The

techniques

in

LIGD

and

GM

are

instances

of

these

templates

A

design

pattern

for

type-indexed

functions

We

document

this

generalisation

as

a

design

pattern

Type-indexed

functions

with

type-indexed

types

We

show

that

with

our

more

general

interpretation

of

the

design

pattern

type-indexed

functions

with

type-indexed

types

are

also

instances

of

the

design

pattern

As

a

consequence

generic

functions

with

generic

types

can

also

be

encoded

in

a

lightweight

manner

Thus

we

remove

one

of

the

main

limitations

of

the

lightweight

approaches

Other

applications

We

present

two

other

interesting

applications

of

the

pattern

PolyP

in

Haskell

and

a

very

flexible

printf

function

The

remainder

of

this

paper

is

structured

as

follows

In

Section

we

review

the

lightweight

approaches

to

generic

programming

In

Section

we

abstract

the

essence

of

the

technique

as

a

design

pattern

Section

presents

two

other

small

applications

of

the

design

pattern

and

Section

uses

it

to

model

type-indexed

functions

with

type-indexed

types

Section

concludes

We

start

by

summarising

the

earlier

work

on

lightweight

approaches

to

generic

programming

underlying

our

generalisation

A

Lightweight

Implementation

of

Generics

and

Dynamics

Cheney

and

Hinze

-LSB-

-RSB-

show

how

to

do

a

kind

of

generic

programming

using

only

the

standard

Hindley-Milner

type

system

extended

with

existential

types

The

index

family

consists

of

hierarchical

sums

and

products

of

integers

and

characters

This

family

is

enough

to

represent

a

large

subset

of

Haskell

datatypes

-LRB-

including

mutually

recursive

and

nested

datatypes

-RRB-

data

Sum

a

b

Inl

a

Inr

b

data

Prod

a

b

Prod

a

b

data

Unit

Unit

This

style

of

generic

programming

requires

a

representation

of

types

as

values

in

order

to

support

typecase

analysis

The

key

idea

of

the

LIGD

paper

is

to

use

a

parametrised

type

as

the

type

representation

ensuring

that

the

type

parameter

reflects

the

type

being

represented

Some

Haskell

implementations

have

recently

been

extended

with

generalised

algebraic

datatypes

-LRB-

GADTs

-RRB-

-LSB-

-RSB-

which

can

be

used

for

this

purpose

but

LIGD

predates

that

extension

and

depends

only

on

existential

quantification

data

Rep

t

RUnit

-LRB-

t

Unit

-RRB-

RInt

-LRB-

t

Int

-RRB-

RChar

-LRB-

t

Char

-RRB-

a

b.

RSum

-LRB-

Rep

a

-RRB-

-LRB-

Rep

b

-RRB-

-LRB-

t

-LRB-

Sum

a

b

-RRB-

-RRB-

a

b.

RProd

-LRB-

Rep

a

-RRB-

-LRB-

Rep

b

-RRB-

-LRB-

t

-LRB-

Prod

a

b

-RRB-

-RRB-

data

a

b

EP

-LCB-

from

a

b

to

b

a

-RCB-

-LRB-

Note

that

the

universal

quantifications

are

in

contravariant

positions

so

act

existentially

-RRB-

The

intention

is

that

the

equivalence

type

a

b

represents

embedding/projection

pairs

witnessing

to

an

isomorphism

between

types

a

and

b

thereby

enforcing

a

correspondence

between

types

t

and

Rep

t.

Of

course

within

Haskell

it

is

not

possible

to

automatically

verify

the

isomorphisms

-LRB-

from

to

id

and

to

from

id

-RRB-

so

these

laws

should

be

externally

checked

Furthermore

we

follow

the

convention

of

ignoring

the

ugly

fact

of

bottom

values

destroying

the

beautiful

theory

of

many

such

isomorphisms

-LSB-

-RSB-

A

common

case

is

with

the

trivial

embedding/projections

self

a

a

self

EP

-LCB-

from

id

to

id

-RCB-

Using

self

we

can

provide

a

set

of

smart

constructors

for

the

Rep

type

yielding

representations

of

types

by

themselves

rUnit

Rep

Unit

rUnit

RUnit

self

rInt

Rep

Int

rInt

RInt

self

rChar

Rep

Char

rChar

RChar

self

rSum

Rep

a

Rep

b

Rep

-LRB-

Sum

a

b

-RRB-

rSum

ra

rb

RSum

ra

rb

self

rProd

Rep

a

Rep

b

Rep

-LRB-

Prod

a

b

-RRB-

rProd

ra

rb

RProd

ra

rb

self

Using

these

smart

constructors

we

can

build

representations

for

recursive

datatypes

by

making

explicit

the

structure

isomorphism

of

the

datatype

For

instance

the

isomorphism

defining

lists

is

-LSB-

a

-RSB-

a

-LSB-

a

-RSB-

and

so

the

corresponding

type

representation

is

as

follows

rList

a.

Rep

a

Rep

-LSB-

a

-RSB-

rList

ra

RSum

rUnit

-LRB-

rProd

ra

-LRB-

rList

ra

-RRB-

-RRB-

-LRB-

EP

from

to

-RRB-

where

from

-LSB-

-RSB-

Inl

Unit

from

-LRB-

x

xs

-RRB-

Inr

-LRB-

Prod

x

xs

-RRB-

to

-LRB-

Inl

Unit

-RRB-

-LSB-

-RSB-

to

-LRB-

Inr

-LRB-

Prod

x

xs

-RRB-

-RRB-

x

xs

Note

that

the

representation

of

a

recursive

datatype

is

an

infinite

value

but

because

of

laziness

this

poses

no

problem

Having

constructed

representation

values

for

arbitrary

types

the

final

step

is

to

define

generic

functions

Using

the

representation

as

a

basis

for

structural

case

analysis

it

is

possible

to

simulate

a

typecase

-LSB-

-RSB-

For

example

here

is

a

definition

of

generic

equality

eq

t.

Rep

t

t

t

Bool

eq

-LRB-

RInt

ep

-RRB-

t

t

from

ep

t

from

ep

t

eq

-LRB-

RChar

ep

-RRB-

t

t

from

ep

t

from

ep

t

eq

-LRB-

RUnit

ep

-RRB-

True

eq

-LRB-

RSum

ra

rb

ep

-RRB-

t

t

case

-LRB-

from

ep

t

from

ep

t

-RRB-

of

-LRB-

Inl

x

Inl

y

-RRB-

eq

ra

x

y

-LRB-

Inr

x

Inr

y

-RRB-

eq

rb

x

y

False

eq

-LRB-

RProd

ra

rb

ep

-RRB-

t

t

case

-LRB-

from

ep

t

from

ep

t

-RRB-

of

-LRB-

Prod

x

y

Prod

x

y

-RRB-

eq

ra

x

x

eq

rb

y

y

Using

Haskell

type

classes

it

is

possible

to

make

the

use

of

generic

functions

even

more

convenient

the

class

TypeRep

can

be

used

to

build

values

of

type

Rep

t

implicitly

class

TypeRep

t

where

rep

Rep

t

instance

TypeRep

Unit

where

rep

rUnit

instance

TypeRep

Int

where

rep

rInt

instance

TypeRep

Char

where

rep

rChar

instance

-LRB-

TypeRep

a

TypeRep

b

-RRB-

TypeRep

-LRB-

Sum

a

b

-RRB-

where

rep

rSum

rep

rep

instance

-LRB-

TypeRep

a

TypeRep

b

-RRB-

TypeRep

-LRB-

Prod

a

b

-RRB-

where

rep

rProd

rep

rep

instance

TypeRep

a

TypeRep

-LSB-

a

-RSB-

where

rep

rList

rep

For

example

we

can

now

express

generic

equality

with

an

implicit

rather

than

explicit

dependence

on

the

representation

ceq

t.

TypeRep

t

t

t

Bool

ceq

t

t

eq

rep

t

t

Generics

for

the

Masses

Hinze

's

later

GM

approach

-LSB-

-RSB-

has

a

very

similar

flavour

to

LIGD

however

somewhat

surprisingly

Hinze

shows

how

to

do

generic

programming

strictly

within

Haskell

which

does

not

support

rank-n

types

or

even

existential

types

Nevertheless

there

is

a

close

relationship

between

type

classes

and

polymorphic

records

-LRB-

for

example

one

possible

translation

of

type

classes

into

System

F

uses

polymorphic

records

-RRB-

and

these

require

something

like

existential

types

for

their

encoding

Thus

type

class

instances

can

be

seen

as

implicitly-passed

records

Hinze

uses

this

observation

to

deliver

two

implementations

of

generics

Generic

functions

on

types

The

first

implementation

of

generics

in

GM

-LRB-

from

now

on

-RRB-

can

be

seen

as

a

direct

descendent

of

LIGD

Instead

of

using

a

datatype

with

an

existential

quantification

Hinze

uses

a

type

class

Generic

class

Generic

g

where

unit

g

Unit

sum

-LRB-

TypeRep

a

TypeRep

b

-RRB-

g

-LRB-

Sum

a

b

-RRB-

prod

-LRB-

TypeRep

a

TypeRep

b

-RRB-

g

-LRB-

Prod

a

b

-RRB-

datatype

TypeRep

a

-LRB-

b

a

-RRB-

g

b

char

g

Char

int

g

Int

The

parameter

g

of

the

type

class

represents

the

generic

function

and

each

of

the

member

functions

of

the

type

class

encodes

the

behaviour

of

that

generic

function

for

one

structural

case

Generic

functions

over

user-defined

types

can

also

be

defined

using

the

datatype

type

case

In

this

case

the

isomorphism

between

the

datatype

and

its

structural

representation

must

be

provided

The

type

class

TypeRep

is

used

to

select

the

appropriate

behaviour

of

the

generic

function

based

on

the

type

structure

of

its

argument

The

role

of

this

type

class

is

somewhat

analogous

to

the

synonymous

one

in

Section

One

contrast

with

LIGD

is

that

TypeRep

for

is

not

optional

because

the

type

representations

are

always

implicitly

passed

class

TypeRep

a

where

typeRep

Generic

g

g

a

instance

TypeRep

Unit

where

typeRep

unit

instance

-LRB-

TypeRep

a

TypeRep

b

-RRB-

TypeRep

-LRB-

Sum

a

b

-RRB-

where

typeRep

sum

instance

-LRB-

TypeRep

a

TypeRep

b

-RRB-

TypeRep

-LRB-

Prod

a

b

-RRB-

where

typeRep

prod

instance

TypeRep

Char

where

typeRep

char

instance

TypeRep

Int

where

typeRep

int

For

GM

the

type

class

TypeRep

directly

selects

the

appropriate

behaviour

for

a

particular

structural

case

from

the

generic

function

In

contrast

for

LIGD

the

corresponding

type

class

TypeRep

builds

a

value

as

a

type

representation

for

a

particular

structural

case

and

this

representation

is

then

used

by

a

generic

function

to

select

the

appropriate

behaviour

The

effect

is

the

same

but

GM

is

more

direct

A

new

generic

function

is

defined

via

an

instance

of

Generic

providing

an

implementation

for

each

structural

case

For

instance

the

generic

function

gSize

that

counts

all

the

elements

of

type

Int

and

Char

in

some

structure

could

be

encoded

as

follows

newtype

GSize

a

GSize

-LCB-

appGSize

a

Int

-RCB-

instance

Generic

GSize

where

unit

GSize

-LRB-

-RRB-

sum

GSize

-LRB-

t

case

t

of

Inl

x

gSize

x

Inr

y

gSize

y

-RRB-

prod

GSize

-LRB-

t

case

t

of

Prod

x

y

gSize

x

gSize

y

-RRB-

datatype

iso

GSize

-LRB-

t

gSize

-LRB-

from

iso

t

-RRB-

-RRB-

char

GSize

-LRB-

-RRB-

int

GSize

-LRB-

-RRB-

gSize

TypeRep

a

a

Int

gSize

appGSize

typeRep

A

record

of

type

GSize

a

contains

a

single

function

appGSize

of

type

a

Int

which

can

be

used

to

compute

the

number

of

elements

in

some

structure

of

type

a

The

function

gSize

which

is

the

actual

generic

function

simply

extracts

the

sole

appGSize

field

from

a

record

of

the

appropriate

type

built

automatically

by

typeRep

Generic

functions

on

type

constructors

The

second

implementation

of

generics

in

GM

-LRB-

-RRB-

permits

parametrisation

by

type

constructors

rather

than

by

types

For

example

whereas

the

generic

function

gSize

of

the

previous

section

has

type

a

Int

for

all

first-order

types

a

in

the

type

class

TypeRep

in

this

section

we

show

a

generic

function

gSize

with

type

f

a

Int

for

all

type

constructors

f

in

the

constructor

class

FunctorRep

Lifting

in

this

fashion

introduces

the

possibility

of

ambiguity

a

type

g

-LRB-

f

a

-RRB-

may

be

considered

a

type

constructor

g

applied

to

a

type

f

a

or

the

composition

of

constructors

g

and

f

applied

to

type

a.

Therefore

we

must

explicitly

pass

type

representations

increasing

flexibility

but

decreasing

brevity

This

is

reflected

in

the

analogous

type

class

Generic

where

the

implicitly-passed

TypeRep

contexts

are

now

changed

to

explicitly-passed

functions

class

Generic

g

where

unit

g

Unit

sum

g

a

g

b

g

-LRB-

Sum

a

b

-RRB-

prod

g

a

g

b

g

-LRB-

Prod

a

b

-RRB-

datatype

-LRB-

b

a

-RRB-

g

a

g

b

char

g

Char

int

g

Int

However

this

modification

of

the

type

class

restricts

expressivity

since

the

only

generic

function

we

can

call

is

the

one

being

defined

recursively

Consequently

generic

functions

that

perform

calls

to

other

generic

functions

-LRB-

as

when

defining

generic

membership

in

terms

of

generic

equality

-RRB-

become

harder

to

define

With

the

new

Generic

class

it

is

also

possible

to

build

the

values

for

type

representations

automatically

using

another

type

class

TypeRep

Just

as

with

LIGD

this

class

now

becomes

optional

Alternatively

we

can

use

a

type

class

FunctorRep

to

capture

the

notion

of

unary

type

constructor

or

functor

class

FunctorRep

f

where

functorRep

Generic

g

g

a

g

-LRB-

f

a

-RRB-

We

have

to

define

similar

classes

for

each

arity

of

type

constructor

Generic

functions

are

defined

in

a

very

similar

fashion

to

For

instance

the

type

Count

a

below

represents

a

generic

function

that

counts

zero

for

each

occurrence

of

a

value

of

type

Int

or

Char

in

some

structure

of

type

a.

newtype

Count

a

Count

-LCB-

applyCount

a

Int

-RCB-

instance

Generic

Count

where

unit

Count

-LRB-

-RRB-

sum

a

b

Count

-LRB-

x

case

x

of

Inl

l

applyCount

a

l

Inr

r

applyCount

b

r

-RRB-

prod

a

b

Count

-LRB-

-LRB-

Prod

x

y

-RRB-

applyCount

a

x

applyCount

b

y

-RRB-

datatype

iso

a

Count

-LRB-

x

applyCount

a

-LRB-

from

iso

x

-RRB-

-RRB-

char

Count

-LRB-

-RRB-

int

Count

-LRB-

-RRB-

While

this

function

by

itself

approximates

const

it

is

the

basis

for

other

more

useful

functions

that

really

count

the

number

of

elements

in

some

structure

in

some

way

by

overriding

the

behaviour

of

the

basic

generic

function

for

occurrences

of

the

type

parameter

gSize

FunctorRep

f

f

a

Int

gSize

applyCount

-LRB-

functorRep

-LRB-

Count

-LRB-

-RRB-

-RRB-

-RRB-

The

payback

of

using

FunctorRep

is

that

we

can

define

the

behaviour

of

the

generic

function

for

its

parameters

For

instance

we

could

sum

all

the

integers

in

some

integer-parametrised

datatype

by

using

the

identity

function

to

define

the

behaviour

of

the

generic

function

for

the

type

parameter

gSum

FunctorRep

f

f

Int

Int

gSum

applyCount

-LRB-

functorRep

-LRB-

Count

id

-RRB-

-RRB-

In

LIGD

and

GM

we

are

shown

three

methods

for

implementing

closed

type-indexed

functions

Those

three

variations

give

us

different

expressive

power

and

impose

different

constraints

on

the

type

system

A

choice

of

implementation

techniques

together

with

technical

trade-offs

making

no

one

method

superior

in

all

circumstances

is

characteristic

of

design

patterns

In

this

section

we

introduce

the

TypeCase

design

pattern

capturing

the

different

techniques

for

implementing

closed

type-indexed

functions

The

TypeCase

design

pattern

Intent

Allowing

the

definition

of

closed

type-indexed

functions

Motivation

The

typecase

design

pattern

captures

a

closed-world

view

of

ad-hoc

polymorphism

In

Haskell

the

type

class

system

is

a

mechanism

that

supports

ad-hoc

polymorphism

but

from

an

open-world

point

of

view

they

can

be

extended

with

cases

for

new

datatypes

at

the

cost

of

a

non-extensible

set

of

functions

Under

the

closed-world

assumption

there

is

a

fixed

set

of

type-structural

cases

but

arbitrarily

many

type-indexed

functions

ranging

over

those

cases

An

example

where

the

closed-world

perpective

works

better

than

the

open-world

one

is

generic

programming

in

which

we

take

a

structural

perspective

on

types

as

opposed

to

the

more

traditional

nominal

one

Using

just

a

few

operations

on

types

it

is

possible

to

represent

the

whole

family

of

structural

definitions

of

interest

For

instance

here

is

a

possible

definition

for

a

generic

function

that

counts

all

the

elements

of

some

structure

t

gsize

t

t

Int

gsize

Unit

gsize

Int

gsize

Sum

-LRB-

Inl

x

-RRB-

gsize

x

gsize

Sum

-LRB-

Inr

y

-RRB-

gsize

y

gsize

Prod

-LRB-

Prod

x

y

-RRB-

gsize

x

gsize

y

With

an

open-world

perspective

we

can

present

a

fixed

number

of

type-indexed

definitions

that

range

over

those

few

cases

but

we

can

not

easily

introduce

new

definitions

This

is

clearly

not

appropriate

for

generic

programming

In

fact

what

we

expect

from

a

generic

programming

facility

is

the

ability

to

a

introduce

new

generic

definition

without

affecting

the

surrounding

context

This

is

precisely

what

the

closed-world

perspective

provides

us

Applicability

Use

this

pattern

to

encode

collections

of

definitions

that

are

indexed

by

some

fixed

family

of

types

while

allowing

new

definitions

to

be

added

to

the

collection

without

affecting

modularity

when

a

definition

is

variadic

that

is

it

has

a

variable

number

of

arguments

-LRB-

see

Section

for

an

example

-RRB-

to

try

to

avoid

type-class

trickery

such

as

multiple-parameter

type

classes

functional

dependencies

overlapping

instances

or

even

duplicate

instances

-LRB-

just

consider

a

direct

encoding

of

the

examples

presented

in

the

paper

into

type

classes

-LSB-

-RSB-

-RRB-

to

capture

some

shape

invariants

like

the

ones

captured

by

some

nested

types

or

phantom

types

-LSB-

-RSB-

Structure

See

Figure

Participants

Structural

Cases

a

set

of

datatypes

which

represent

the

possible

structural

cases

for

the

type-indexed

function

Typecase

representing

the

structure

of

a

type-indexed

function

Dispatcher

a

type

class

containing

a

single

function

that

is

responsible

for

dispatching

a

value

of

one

of

the

structural

cases

into

the

corresponding

branch

of

the

typecase

based

on

the

type

of

the

value

Type-indexed

function

defining

the

type-indexed

function

using

an

instance

of

the

typecase

Collaborations

The

typecase

uses

the

structural

cases

in

order

to

create

a

corresponding

number

of

cases

that

can

be

used

to

define

the

type-indexed

function

The

dispatcher

uses

the

structural

cases

in

order

to

create

a

corresponding

number

of

instances

that

will

forward

some

value

of

that

family

of

structural

cases

into

the

corresponding

case

in

the

typecase

component

The

type-indexed

function

-LRB-

TIF

-RRB-

uses

an

instance

of

the

typecase

in

order

to

implement

the

desired

functionality

for

the

type-indexed

function

Implementation

Typically

a

typecase

component

is

created

using

the

structural

cases

There

are

three

main

variations

for

the

implementation

of

a

typecase

two

of

them

are

based

on

type

classes

and

the

other

one

on

a

smart

datatype

A

smart

datatype

is

a

parametrised

type

where

the

type

parameters

are

dependent

on

the

constructors

The

idea

of

a

smart

datatype

can

be

represented

in

various

forms

existential

datatypes

with

an

equivalence

type

-LRB-

la

LIGD

-RRB-

GADTs

phantom

types

among

others

The

goal

of

this

design

pattern

is

to

simulate

a

closed

type-indexed

function

In

general

a

type-indexed

function

f

has

the

following

structure

f

t

d

d

k

f

t

a

a

i

x

x

e

f

t

m

z

z

j

x

x

mn

e

m

The

type

signature

tells

us

that

f

has

one

type

parameter

t

and

optional

type

parameters

d

d

k

with

the

same

structure

and

kind

as

t

The

type

of

the

TIF

may

depend

on

t

and

d

d

k

We

should

note

that

this

is

not

the

same

as

having

a

TIF

with

multiple

type

arguments

There

is

no

problem

in

principle

in

having

multiple-parameter

type

arguments

but

it

would

lead

to

an

explosion

in

the

number

of

typecases

This

would

be

a

generalisation

of

this

design

pattern

For

simplicity

we

will

only

consider

type

parameters

with

the

same

structure

The

usefulness

of

this

simpler

case

is

reflected

in

applications

such

as

generic

map

where

the

input

and

output

structures

of

the

generic

map

function

are

the

same

The

body

of

f

contains

-LRB-

at

least

-RRB-

m

branches

providing

the

behaviour

of

the

TIF

for

each

member

of

the

family

of

types

t

-LRB-

that

is

t

a

a

i

t

m

z

z

j

-RRB-

This

family

of

types

corresponds

to

the

structural

cases

participant

of

the

design

pattern

For

each

branch

of

the

definition

we

bind

possible

variables

x

x

x

x

mn

and

define

each

typecase

of

f

with

e

e

m

We

now

discuss

the

three

main

variations

of

the

design

pattern

Smart

datatypes

This

variation

is

inspired

by

the

LIGD

approach

Hindley-Milner

typing

extended

with

existential

datatypes

-LRB-

supported

in

most

Haskell

compilers

-RRB-

is

enough

to

encode

it

However

with

extensions

such

as

GADTs

-LRB-

supported

by

GHC

-RRB-

the

encoding

becomes

much

more

direct

Unfortu-nately

neither

of

those

extensions

conforms

to

Haskell

We

will

present

this

version

of

the

design

pattern

using

a

GADT

syntax

for

simplicity

Using

the

structural

cases

given

by

t

a

a

i

t

m

z

z

j

we

can

derive

the

typecase

and

dispatcher

seen

in

Figure

Since

there

are

m

structural

cases

in

a

standard

instance

of

the

design

pattern

one

would

create

m

constructors

c

t

c

t

m

and

also

m

instances

for

Rep

TIFs

can

now

be

defined

using

those

components

by

creating

some

function

f

that

takes

a

first

argument

of

type

Rep

and

returns

a

value

of

type

Smart

Datatype

Implicit/Explicit

Representations

Typecase

data

t

d

d

k

where

c

t

-LRB-

a

a

i

-RRB-

-LRB-

t

a

a

i

-RRB-

d

d

c

t

m

-LRB-

z

z

j

-RRB-

-LRB-

t

m

z

z

j

-RRB-

d

d

mk

class

-LRB-

g

k

-RRB-

where

case

t

-LRB-

a

a

i

-RRB-

g

-LRB-

t

a

a

i

-RRB-

d

d

case

t

m

-LRB-

z

z

j

-RRB-

g

-LRB-

t

m

z

z

j

-RRB-

d

d

mk

Dispatcher

class

Rep

t

d

d

k

where

rep

Rep

t

d

d

k

instance

-LRB-

a

a

i

-RRB-

Rep

-LRB-

t

a

a

i

-RRB-

d

d

where

rep

c

t

rep

i

instance

-LRB-

z

z

j

-RRB-

Rep

-LRB-

t

m

z

z

j

-RRB-

d

d

mk

where

rep

c

t

m

rep

j

class

Rep

t

d

d

k

where

rep

g

g

t

d

d

k

instance

-LRB-

a

a

i

-RRB-

Rep

-LRB-

t

a

a

i

-RRB-

d

d

where

rep

case

t

-LCB-

rep

i

-RCB-

instance

-LRB-

z

z

j

-RRB-

Rep

-LRB-

t

m

z

z

j

-RRB-

d

d

mk

where

rep

case

t

m

-LCB-

rep

j

-RCB-

Type-indexed

function

f

t

d

d

k

f

-LRB-

c

t

r

a

r

a

i

-RRB-

x

x

-LSB-

-LSB-

e

-RSB-

-RSB-

f

-LRB-

c

t

m

r

z

r

z

j

-RRB-

x

x

mn

-LSB-

-LSB-

e

m

-RSB-

-RSB-

f

Rep

t

d

d

k

f

f

rep

newtype

F

t

d

d

k

F

-LCB-

f

-RCB-

f

Rep

t

d

d

k

f

f

rep

instance

Rep

F

where

case

t

-LCB-

r

a

r

a

i

-RCB-

x

x

-LSB-

-LSB-

e

-RSB-

-RSB-

case

t

m

-LCB-

r

z

r

z

j

-RCB-

x

x

mn

-LSB-

-LSB-

e

m

-RSB-

-RSB-

Figure

The

structure

of

the

TypeCase

design

pattern

The

dispatcher

component

is

optional

in

this

variation

The

TIFs

created

with

this

variation

are

fully

closed

to

extension

no

customisation

is

possible

This

means

that

if

we

want

to

add

extra

functionality

we

need

to

modify

the

smart

datatype

-LRB-

and

the

dispatcher

if

we

have

one

-RRB-

However

TIFs

that

call

other

TIFs

are

trivial

to

achieve

there

is

no

need

for

tupling

Implicit

representations

The

implicit

representation

version

of

the

design

pattern

is

inspired

by

Perhaps

surprisingly

some

implementations

of

this

instance

require

only

Haskell

However

if

we

need

to

have

structurally-dependent

variables

then

we

also

require

multiple-parameter

type

classes

Proceeding

in

a

similar

fashion

to

the

smart

datatype

approach

we

use

the

structural

cases

to

derive

the

typecase

and

dispatcher

seen

in

Figure

Again

because

we

have

m

structural

cases

we

create

m

functions

case

t

case

t

m

and

m

instances

of

Rep

The

dispatcher

is

not

an

optional

component

it

always

needs

to

be

defined

in

this

variation

As

with

the

smart

datatype

variation

TIFs

defined

in

this

way

are

fully

closed

to

extension

and

calls

to

other

TIFs

are

trivial

Explicit

representations

The

explicit

representation

variation

of

the

design

pattern

is

inspired

by

Like

the

implicit

approach

Haskell

is

enough

to

handle

the

simpler

forms

-LRB-

one

type

parameter

-RRB-

However

if

we

discard

the

optional

dispatcher

then

Haskell

can

handle

all

forms

Using

the

structural

cases

to

derive

the

typecase

and

dispatcher

seen

in

Figure

we

would

obtain

a

very

similar

structure

to

the

implicit

representation

version

The

most

noticeable

difference

is

that

with

the

explicit

representation

the

definition

of

rep

needs

to

provide

the

corresponding

case

function

with

the

representations

for

each

of

its

type

parameters

The

second

difference

is

that

which

corresponds

to

the

representations

of

the

type

parameters

reflects

the

fact

that

we

are

providing

explicit

representations

Thus

corresponds

in

this

instance

to

explicit

arguments

of

the

function

while

with

the

implicit

representation

it

corresponds

to

-LRB-

implicitly

passed

-RRB-

type

class

constraints

The

dispatcher

is

an

optional

component

Variations

of

this

instance

of

the

design

pattern

can

also

be

found

in

the

literature

-LSB-

-RSB-

as

described

in

Section

TIFs

defined

in

this

fashion

are

not

fully

closed

to

extension

it

is

possible

to

override

default

behaviour

However

the

extra

flexibility

comes

at

a

cost

recursive

calls

to

other

TIFs

are

not

possible

One

common

solution

for

this

problem

is

to

tuple

together

into

a

record

the

mutually-dependent

functions

Another

possibility

would

be

to

have

a

notion

of

dependencies

if

a

TIF

f

requires

calls

to

another

TIF

g

then

the

record

that

defines

f

has

a

field

that

is

an

instance

of

g.

Although

this

work

is

quite

tedious

L

h

-LSB-

-RSB-

shows

how

a

type

system

can

lighten

the

burden

An

associated

problem

for

TIFs

in

this

setting

is

the

issue

of

composability

If

two

TIFs

are

defined

using

different

instances

-LRB-

this

is

they

are

not

tupled

together

-RRB-

then

we

can

not

in

a

straightforward

manner

use

the

same

representation

to

compose

them

To

illustrate

the

problem

consider

newtype

F

v

v

n

F

-LCB-

f

-RCB-

newtype

G

v

v

n

G

-LCB-

g

-RCB-

instance

Generic

F

where

instance

Generic

G

where

Now

let

us

suppose

that

we

define

a

type-indexed

abstraction

-LRB-

that

is

a

function

that

uses

one

or

more

TIFs

and

is

not

defined

over

the

structure

of

types

-RRB-

h

rep

f

rep

g

rep

The

interpretation

of

this

definition

as

a

type-indexed

function

could

be

thought

of

as

h

a

f

a

g

a

While

this

is

a

perfectly

reasonable

interpretation

in

practice

f

requires

inconsistent

types

F

v

v

n

and

G

v

v

n

for

rep

F

and

G

are

two

different

type

constructors

so

in

a

Hindley-Milner

type

system

unification

obviously

fails

However

F

and

G

do

have

something

in

common

In

particular

they

are

both

instances

of

Generic

So

in

Haskell

extended

with

higher-order

polymorphism

we

can

capture

this

relation

with

a

type

thus

providing

a

possible

solution

for

the

problem

of

composability

h

-LRB-

g.

Generic

g

g

v

v

n

-RRB-

h

rep

f

rep

g

rep

We

should

note

that

even

though

we

have

presented

three

main

variations

of

the

design

pattern

the

concept

of

a

design

pattern

is

by

itself

quite

informal

and

thus

prone

to

different

interpretations

For

instance

as

we

will

see

later

applications

of

the

pattern

-LRB-

such

as

GM

-RRB-

can

have

more

type

cases

than

there

are

datatype

variants

because

some

cases

overlap

It

is

important

to

note

that

depending

on

the

context

of

a

problem

a

design

pattern

can

be

adapted

to

better

fit

that

problem

We

present

two

applications

of

the

design

pattern

In

Section

still

within

the

context

of

generic

programming

we

show

how

one

can

build

a

library

inspired

by

PolyP

-LSB-

-RSB-

but

working

in

Haskell

In

Section

we

present

a

very

flexible

version

of

a

C-style

printf

function

Light

PolyP

It

probably

comes

as

no

surprise

to

the

reader

that

the

technique

introduced

in

GM

and

LIGD

can

be

applied

to

other

generic

programming

approaches

as

well

PolyP

was

one

of

the

first

attempts

to

produce

a

generic

programming

language

It

is

a

simpler

language

than

Generic

Haskell

working

in

a

much

more

restricted

family

of

datatypes

namely

one-parameter

regular

types

But

this

restriction

allows

stronger

properties

to

be

stated

its

simplicity

and

strong

theoretical

background

make

it

an

appropriate

language

for

teaching

both

the

theory

-LSB-

-RSB-

and

practice

of

generic

programming

Our

proposal

Light

PolyP

encourages

this

because

no

external

PolyP

compiler

is

required

-LRB-

although

one

might

still

be

desirable

for

a

more

convenient

syntax

-RRB-

Norell

-LSB-

-RSB-

shows

how

to

use

the

Haskell

type

class

system

-LRB-

extended

with

multiple-parameter

type

classes

and

functional

dependencies

-RRB-

to

obtain

first-class

PolyP

generic

functions

in

Haskell

In

this

section

we

will

present

a

lighter

version

of

PolyP

requiring

only

Haskell

-LRB-

without

extensions

such

as

multiple-parameter

type

classes

and

functional

dependencies

-RRB-

but

with

the

same

expressive

power

Instead

of

using

sums

of

products

like

LAGP

or

Generic

Haskell

PolyP

uses

lifted

pattern

functors

as

structural

cases

The

pattern

functors

Empty

Plus

and

Prod

have

counterparts

in

LAGP

The

pattern

functors

Rep

and

Par

correspond

respectively

to

the

recursive

argument

and

the

parameter

of

the

unary

regular

datatype

The

pattern

functor

Const

t

for

some

type

t

represents

the

constant

functor

and

Comp

handles

the

composition

of

functors

required

for

regular

types

data

Empty

p

r

Empty

data

Plus

g

h

p

r

Inl

-LRB-

g

p

r

-RRB-

Inr

-LRB-

h

p

r

-RRB-

data

Prod

g

h

p

r

Prod

-LRB-

g

p

r

-RRB-

-LRB-

h

p

r

-RRB-

newtype

Par

p

r

Par

-LCB-

unPar

:p

-RCB-

newtype

Rec

p

r

Rec

-LCB-

unRec

r

-RCB-

newtype

Comp

d

h

p

r

Comp

-LCB-

unComp

d

-LRB-

h

p

r

-RRB-

-RCB-

newtype

Const

t

p

r

Const

-LCB-

unConst

t

-RCB-

The

equivalence

type

is

used

to

establish

the

isomorphism

between

a

regular

datatype

and

its

top-level

structure

The

embedding/projection

functions

are

traditionally

called

inn

and

out

data

Iso

a

b

Iso

-LCB-

inn

a

b

out

b

a

-RCB-

listIso

Iso

inL

outL

where

inL

-LRB-

Inl

Empty

-RRB-

-LSB-

-RSB-

inL

-LRB-

Inr

-LRB-

Prod

-LRB-

Par

x

-RRB-

-LRB-

Rec

xs

-RRB-

-RRB-

-RRB-

x

xs

outL

-LSB-

-RSB-

Inl

Empty

outL

-LRB-

x

xs

-RRB-

Inr

-LRB-

Prod

-LRB-

Par

x

-RRB-

-LRB-

Rec

xs

-RRB-

-RRB-

In

PolyP

no

generic

customisation

is

allowed

thus

we

can

use

an

implicit

representation

version

of

the

design

pattern

and

consequently

it

is

possible

for

one

generic

function

to

use

other

generic

functions

in

its

definition

The

typecase

component

corresponds

to

class

Generic

f

where

empty

f

Empty

plus

-LRB-

Rep

g

Rep

h

-RRB-

f

-LRB-

Plus

g

h

-RRB-

prod

-LRB-

Rep

g

Rep

h

-RRB-

f

-LRB-

Prod

g

h

-RRB-

par

f

Par

rec

f

Rec

comp

-LRB-

Functor

d

Rep

h

-RRB-

f

-LRB-

Comp

d

h

-RRB-

constant

f

-LRB-

Const

t

-RRB-

The

dispatcher

simply

selects

the

corresponding

case

based

on

the

type

of

the

argument

of

the

generic

function

g.

class

Rep

g

where

rep

Generic

f

f

g

instance

Rep

Empty

where

rep

empty

instance

-LRB-

Rep

g

Rep

h

-RRB-

Rep

-LRB-

Plus

g

h

-RRB-

where

rep

plus

instance

-LRB-

Rep

g

Rep

h

-RRB-

Rep

-LRB-

Prod

g

h

-RRB-

where

rep

prod

instance

Rep

Par

where

rep

par

instance

Rep

Rec

where

rep

rec

instance

-LRB-

Functor

d

Rep

h

-RRB-

Rep

-LRB-

Comp

d

h

-RRB-

where

rep

comp

instance

Rep

-LRB-

Const

t

-RRB-

where

rep

constant

Like

GM

defining

a

generic

function

is

a

matter

of

declaring

a

record

with

a

single

field

a

function

of

the

appropriate

type

As

an

example

we

could

define

the

map

operation

for

binary

functors

as

follows

newtype

a

b

c

d

f

-LCB-

-LRB-

a

c

-RRB-

-LRB-

b

d

-RRB-

f

a

b

f

c

d

-RCB-

instance

Generic

-LRB-

a

b

c

d

-RRB-

where

empty

-LRB-

Empty

-RRB-

plus

-LRB-

f

g

t

case

t

of

Inl

x

Inl

-LRB-

f

g

x

-RRB-

Inr

y

Inr

-LRB-

f

g

y

-RRB-

-RRB-

prod

-LRB-

f

g

t

case

t

of

Prod

x

y

Prod

-LRB-

f

g

x

-RRB-

-LRB-

f

g

y

-RRB-

-RRB-

par

-LRB-

f

g

-LRB-

Par

t

-RRB-

Par

-LRB-

f

t

-RRB-

-RRB-

rec

-LRB-

f

g

-LRB-

Rec

t

-RRB-

Rec

-LRB-

g

t

-RRB-

-RRB-

comp

-LRB-

f

g

-LRB-

Comp

t

-RRB-

Comp

-LRB-

fmap

-LRB-

f

g

-RRB-

t

-RRB-

-RRB-

constant

-LRB-

-LRB-

Const

t

-RRB-

-LRB-

Const

t

-RRB-

-RRB-

Rep

f

-LRB-

a

c

-RRB-

-LRB-

b

d

-RRB-

f

a

b

f

c

d

rep

With

it

is

now

possible

to

define

several

widely-applicable

recursion

operators

-LSB-

-RSB-

using

PolyP

For

example

the

cata-morphism

operator

could

be

defined

as

cata

iso

f

f

id

-LRB-

cata

iso

f

-RRB-

out

iso

Note

that

one

must

give

explicitly

the

isomorphism

that

converts

between

the

datatype

and

its

representation

This

contrasts

with

the

original

PolyP

approach

in

which

that

translation

is

inferred

This

is

the

common

trade-off

of

brevity

for

flexibility

being

forced

to

state

the

isomorphism

allows

the

programmer

to

choose

a

different

one

giving

something

analogous

to

Wadler

's

ideas

about

views

-LSB-

-RSB-

We

might

say

that

this

style

of

generic

programming

is

isomorphism-parametrised

instead

of

datatype-parametrised

In

the

original

PolyP

the

polytypic

construct

provides

a

convenient

syntax

for

encoding

generic

functions

Furthermore

combinators

for

pointfree

programming

may

be

provided

making

generic

definitions

even

more

compact

These

combinators

are

just

normal

Haskell

functions

and

so

there

is

no

problem

in

implementing

them

in

pure

Haskell

but

to

keep

the

example

short

we

have

stuck

with

pointwise

definitions

The

advantages

of

this

translation

when

compared

with

the

one

proposed

in

-LSB-

-RSB-

are

that

it

requires

only

Haskell

and

that

the

types

of

the

generic

functions

are

much

closer

to

what

one

would

expect

In

Norell

's

translation

the

type

class

constraints

posed

some

problems

because

both

the

two-parameter

class

FunctorOf

and

the

classes

for

the

generic

functions

propagated

throughout

the

code

With

the

Light

PolyP

approach

only

instances

of

Rep

propagate

leading

usually

to

just

one

type

class

constraint

Printf

The

C-style

printf

function

which

takes

a

variable

number

of

parameters

has

always

been

a

challenge

for

programmers

using

strongly

and

statically

typed

languages

The

problem

with

printf

is

that

in

its

true

essence

it

requires

dependent

types

This

happens

because

the

value

of

the

format

string

determines

the

type

of

the

function

However

it

has

been

shown

by

Danvy

-LSB-

-RSB-

that

by

changing

the

representation

of

the

control

string

it

is

possible

to

encode

printf

in

any

language

supporting

a

standard

Hindley-Milner

type

system

A

solution

using

explicit

representations

In

this

section

we

will

demonstrate

that

Danvy

's

solution

is

another

instance

of

the

TypeCase

design

pattern

using

an

explicit

representation

Furthermore

we

will

show

a

new

use

of

the

printf

function

by

making

use

of

the

fact

that

we

can

-LRB-

in

some

cases

-RRB-

infer

the

format

string

Danvy

's

original

solution

had

the

following

combinators

lit

String

-LRB-

String

a

-RRB-

String

a

lit

x

k

s

k

-LRB-

s

x

-RRB-

eol

-LRB-

String

a

-RRB-

String

a

eol

k

s

k

-LRB-

s

n

-RRB-

int

-LRB-

String

a

-RRB-

String

Int

a

int

k

s

x

k

-LRB-

s

show

x

-RRB-

str

-LRB-

String

a

-RRB-

String

String

a

str

k

s

x

k

-LRB-

s

x

-RRB-

eod

String

String

eod

id

If

we

capture

all

the

occurrences

of

the

form

String

t

with

a

newtype

Printf

and

modify

the

definitions

in

order

to

reflect

this

newtype

we

obtain

the

following

code

newtype

Printf

t

Printf

-LCB-

printfApp

String

t

-RCB-

lit

String

Printf

a

Printf

a

lit

x

k

Printf

-LRB-

s

printfApp

k

-LRB-

s

x

-RRB-

-RRB-

eol

Printf

a

Printf

a

eol

k

Printf

-LRB-

s

printfApp

k

-LRB-

s

n

-RRB-

-RRB-

int

Printf

a

Printf

-LRB-

Int

a

-RRB-

int

k

Printf

-LRB-

s

x

printfApp

k

-LRB-

s

show

x

-RRB-

-RRB-

str

Printf

a

Printf

-LRB-

String

a

-RRB-

str

k

Printf

-LRB-

s

x

printfApp

k

-LRB-

s

x

-RRB-

-RRB-

eod

Printf

String

eod

Printf

id

Taking

one

step

further

we

can

now

abstract

over

Printf

and

create

a

type

class

that

replaces

it

with

some

functor

f

class

Format

f

where

lit

String

f

r

f

r

eol

f

r

f

r

int

f

r

f

-LRB-

Int

r

-RRB-

str

f

r

f

-LRB-

String

r

-RRB-

eod

f

String

With

this

last

transformation

we

can

start

seeing

an

instance

of

the

TypeCase

design

pattern

The

structural

cases

participant

consists

of

functions

of

the

form

Int

r

or

String

r

or

a

String

lit

and

eol

are

overlapping

cases

The

class

Format

constitutes

the

typecase

participant

Because

the

dispatcher

is

optional

in

explicit

versions

of

the

design

pattern

there

is

no

obligation

to

define

it

Now

using

the

newtype

Printf

we

can

define

an

instance

of

Format

that

implements

the

functionality

of

printf

instance

Format

Printf

where

lit

x

k

Printf

-LRB-

s

printfApp

k

-LRB-

s

x

-RRB-

-RRB-

eol

k

Printf

-LRB-

s

printfApp

k

-LRB-

s

n

-RRB-

-RRB-

int

k

Printf

-LRB-

s

x

printfApp

k

-LRB-

s

show

x

-RRB-

-RRB-

str

k

Printf

-LRB-

s

x

printfApp

k

-LRB-

s

x

-RRB-

-RRB-

eod

Printf

id

The

final

touch

is

provided

by

the

definition

of

printf

in

terms

of

printfApp

The

printf

function

is

expected

to

receive

the

formatting

argument

of

type

Printf

t

as

its

first

parameter

The

parameter

t

defines

the

type

of

printf

which

can

involve

a

variable

number

of

arguments

Analysing

the

type

of

printfApp

we

see

that

the

first

parameter

is

the

formatting

argument

the

resulting

type

is

the

type

that

we

expect

for

printf

and

there

is

a

second

argument

which

is

a

String

Now

what

does

that

String

represent

Danvy

's

solution

uses

a

continuation-passing

style

and

the

second

argument

of

printfApp

corresponds

to

the

value

fed

to

the

initial

continuation

Thus

using

the

string

for

that

argument

does

the

trick

printf

Printf

t

t

printf

p

printfApp

p

We

have

shown

informally

that

Danvy

's

solution

is

indeed

an

instance

of

the

TypeCase

design

pattern

However

some

questions

might

be

asked

at

this

point

Do

we

really

need

to

create

a

class

in

order

to

implement

printf

What

other

instances

of

the

class

would

we

be

able

to

provide

In

fact

there

are

not

many

other

uses

for

the

type

class

printf

seems

to

be

the

only

natural

instance

Perhaps

we

could

consider

scanf

another

C

function

that

uses

the

same

format

string

but

the

derived

type

for

scanf

would

be

different

and

so

it

is

not

possible

to

reuse

the

same

type

class

Another

possibility

would

be

considering

other

versions

of

printf

such

as

one

for

the

IO

monad

However

if

we

think

that

printf

is

really

the

only

useful

instance

of

the

type

class

why

not

get

rid

of

the

type

class

all

together

A

design

pattern

is

a

flexible

design

and

depending

on

the

context

of

the

problem

it

can

be

adapted

to

fit

the

problem

If

a

type-indexed

function

is

used

at

just

one

type

index

it

is

reasonable

to

simplify

the

pattern

and

eliminate

the

type

class

The

result

would

be

the

specialised

solution

using

the

newtype

Printf

t

presented

before

We

could

go

even

further

and

argue

that

Danvy

's

original

solution

is

already

an

instance

of

the

design

pattern

corresponding

to

one

further

simplification

of

the

design

pattern

namely

getting

rid

of

the

newtype

An

alternative

solution

using

smart

datatypes

In

the

previous

section

we

have

argued

that

Danvy

's

version

of

printf

is

an

instance

of

the

TypeCase

design

pattern

However

Danvy

's

solution

and

explanation

for

printf

is

not

perhaps

very

intuitive

to

understand

In

this

section

we

take

a

different

perpective

and

will

look

at

the

formatting

parameter

of

printf

as

a

special

kind

of

list

This

perpective

corresponds

to

an

instance

of

the

design

pattern

using

a

smart

datatype

The

datatype

-LRB-

the

typecase

participant

-RRB-

encodes

a

list

which

has

an

empty

case

that

ponds

to

the

combinator

eod

and

a

number

of

recursive

cases

that

correspond

to

lit

eol

int

and

str

data

Printf

t

where

Lit

String

Printf

t

Printf

t

Eol

Printf

t

Printf

t

Int

Printf

t

Printf

-LRB-

Int

t

-RRB-

Str

Printf

t

Printf

-LRB-

String

t

-RRB-

Eod

Printf

String

Informally

speaking

we

have

reused

the

types

from

the

newtype

solution

and

lifted

the

functions

to

constructors

However

using

a

datatype

instead

of

a

number

of

functions

makes

it

easier

to

view

the

format

parameter

of

printf

as

a

list

For

instance

the

Lit

constructor

takes

the

literal

string

that

we

wish

to

print

and

also

the

list

corresponding

to

the

rest

of

the

format

parameter

of

printf

The

printfApp

from

the

previous

section

would

in

this

setting

correspond

to

a

dependently-typed

function

-LRB-

in

the

sense

that

the

types

of

its

branches

are

determined

by

the

constructors

used

to

perform

pattern

matching

-RRB-

printfApp

Printf

t

String

t

printfApp

-LRB-

Lit

x

k

-RRB-

s

printfApp

k

-LRB-

s

x

-RRB-

printfApp

-LRB-

Eol

k

-RRB-

s

printfApp

k

-LRB-

s

n

-RRB-

printfApp

-LRB-

Int

k

-RRB-

s

-LRB-

x

printfApp

k

-LRB-

s

show

x

-RRB-

-RRB-

printfApp

-LRB-

Str

k

-RRB-

s

-LRB-

x

printfApp

k

-LRB-

s

x

-RRB-

-RRB-

printfApp

Eod

s

s

The

final

step

is

to

define

printf

Little

effort

is

required

we

just

need

to

copy

the

definition

of

printf

from

the

previous

section

The

only

apparent

difference

between

the

two

versions

is

that

where

the

first

version

uses

functions

like

lit

and

int

this

version

uses

constructors

like

Lit

and

Int

However

despite

the

similarity

of

the

two

solutions

their

expressive

power

is

not

the

same

The

smart

datatype

solution

in

this

section

is

fully

closed

to

extension

That

is

in

order

to

add

another

case

in

the

formatting

list

such

as

a

constructor

Chr

that

handles

characters

we

would

need

to

modify

the

GADT

itself

On

the

other

hand

the

solution

in

the

previous

section

using

the

explicit

version

of

the

design

pattern

allows

some

form

of

extensibility

Adding

a

new

case

for

printf

that

handles

characters

corresponds

to

adding

a

new

function

which

could

even

be

in

a

different

module

Making

use

of

a

dispatcher

The

two

solutions

that

we

presented

did

not

make

any

use

of

a

dispatcher

In

this

section

we

will

show

how

the

dispatcher

can

be

useful

The

version

of

the

dispatcher

presented

here

is

for

the

explicit

representation

solution

in

Section

but

could

be

easily

adapted

to

the

smart

datatype

solution

in

Section

Suppose

that

we

want

to

define

a

function

that

prints

a

pair

of

integers

Equipped

with

printf

we

could

try

to

encode

that

with

either

one

of

the

following

two

functions

printPair

x

y

printf

fmt

-LRB-

x

y

-RRB-

where

fmt

str

int

str

int

str

eod

x

y

printf

fmt

x

y

where

fmt

lit

-LRB-

int

lit

int

lit

-RRB-

eod

The

function

printPair

tackles

the

problem

using

a

printf

that

takes

a

format

argument

expecting

five

arguments

three

strings

and

two

integers

The

function

on

the

other

hand

makes

use

of

the

fact

that

the

string

arguments

are

constants

and

uses

lit

instead

Thus

in

this

case

printf

takes

the

format

argument

and

two

integer

arguments

Although

relatively

compact

the

format

argument

is

not

as

convenient

to

use

as

it

would

be

in

C

where

one

would

write

something

like

-LRB-

d

d

-RRB-

The

role

of

the

dispatcher

is

to

infer

automatically

the

corresponding

type

representation

for

some

type

t

In

the

case

of

printf

it

is

not

possible

to

infer

all

possible

representations

Consider

for

instance

the

end

of

line

case

eol

f

r

f

r

which

takes

an

existing

format

with

some

type

r

adds

a

newline

and

returns

a

format

of

the

same

type

Clearly

there

is

no

way

to

deduce

that

there

is

an

occurrence

of

eol

based

on

the

type

alone

Similarly

the

lit

case

has

no

effect

on

the

type

Nevertheless

the

other

more

type-informative

cases

of

printf

can

be

inferred

class

Rep

t

where

rep

Format

f

f

t

instance

Rep

String

where

rep

eod

instance

Rep

r

Rep

-LRB-

Int

r

-RRB-

where

rep

int

rep

instance

Rep

r

Rep

-LRB-

String

r

-RRB-

where

rep

str

rep

We

should

note

that

these

instance

declarations

are

outside

the

scope

of

Haskell

types

are

used

where

type

variables

should

occur

However

this

is

a

quite

mild

extension

and

is

supported

by

most

Haskell

compilers

Making

use

of

the

fact

that

now

we

can

infer

some

cases

of

the

string

format

we

could

define

printPair

Int

Int

String

printPair

x

y

printf

rep

-LRB-

x

y

-RRB-

printTrio

Int

Int

Int

String

printTrio

x

y

z

printf

rep

-LRB-

x

y

z

-RRB-

The

function

printPair

does

the

same

as

before

However

with

this

new

definition

the

format

directive

is

automatically

inferred

The

function

printTrio

is

doing

the

same

as

printPair

except

that

it

does

it

for

triples

We

should

emphasise

that

the

occurrences

of

printf

in

those

two

functions

use

different

numbers

of

arguments

We

should

also

mention

that

in

some

situations

we

will

need

to

provide

explicit

types

otherwise

the

type

checker

would

not

be

able

to

infer

the

correct

instances

of

the

type

class

Rep.

This

use

of

printf

seems

to

be

practical

and

for

this

simple

version

of

it

we

might

even

argue

that

everything

that

we

could

do

with

a

manually-provided

parameter

could

be

done

with

an

automatically-inferred

one

We

simply

do

not

need

lit

and

eol

because

those

can

be

simulated

using

str

-LRB-

with

of

course

extra

String

arguments

-RRB-

Nevertheless

if

we

decided

to

go

for

a

more

powerful

version

of

printf

this

might

not

be

the

case

Consider

for

instance

the

formatting

directive

In

this

case

the

number

is

specifying

the

minimum

width

of

the

string

that

represents

that

number

If

we

wanted

to

allow

this

kind

of

behaviour

we

could

add

an

extra

parameter

of

type

Int

to

the

int

case

However

the

problem

now

is

to

choose

a

value

for

that

parameter

when

we

automatically

build

the

format

directive

In

this

case

we

need

to

use

some

default

value

-LRB-

for

instance

-RRB-

However

we

are

no

longer

able

for

all

possible

cases

to

simulate

the

functionality

of

printf

with

manual

format

strings

using

only

automatically-built

ones

Until

now

we

have

been

discussing

type-indexed

functions

that

is

families

of

functions

indexed

by

types

We

turn

now

to

type-indexed

types

that

is

families

of

types

indexed

by

types

In

the

context

of

generic

programming

we

call

these

generic

types

Generic

functions

with

generic

types

are

functions

that

have

different

result

types

for

each

structural

case

In

this

section

we

will

show

how

to

implement

type-indexed

types

as

another

variation

of

the

TypeCase

design

pattern

We

do

this

by

translating

a

standard

example

of

Generic

Haskell

-LSB-

-RSB-

namely

generic

tries

-LSB-

-RSB-

into

our

approach

Encoding

type-indexed

types

Section

presents

templates

for

encoding

type-indexed

functions

In

this

section

we

show

how

to

translate

a

type-indexed

type

into

an

instance

of

the

TypeCase

design

pattern

In

general

a

type-indexed

type

has

the

form

t

t

a

a

i

d

d

t

m

z

z

j

d

d

mn

m

where

is

the

type-level

function

that

defines

the

type-indexed

type

t

is

the

family

of

types

-LRB-

or

type

constructors

-RRB-

-LRB-

t

a

a

i

-RRB-

-LRB-

t

m

z

z

j

-RRB-

of

kind

that

corresponds

to

the

structural

cases

of

the

design

pattern

and

finally

is

the

kind

of

t

For

each

type

that

is

member

of

that

family

we

have

a

corresponding

branch

for

The

type-level

lambda

abstraction

on

the

right

side

of

each

branch

is

optional

and

corresponds

to

possible

parametrically

polymorphic

variables

d

d

n

that

the

type-indexed

type

might

depend

on

Finally

m

corresponds

to

the

family

of

types

-LRB-

or

type

constructors

-RRB-

that

defines

the

type-indexed

type

Type

class

translation

We

can

now

derive

an

instance

of

the

TypeCase

design

pattern

to

capture

type-indexed

functions

with

type-indexed

types

The

typecase

participant

for

instances

of

the

design

pattern

using

either

implicit

or

explicit

representations

could

be

defined

as

follows

class

-LRB-

g

-RRB-

where

case

t

-LRB-

a

a

i

-RRB-

g

-LRB-

t

a

a

i

-RRB-

d

d

case

t

m

-LRB-

z

z

j

-RRB-

g

-LRB-

t

m

z

z

j

-RRB-

d

d

mn

m

We

reuse

the

name

for

the

name

of

the

type

class

that

encodes

the

typecase

component

The

parameter

g

is

a

type

constructor

with

kind

where

is

the

literal

occurrence

of

-LRB-

if

we

were

to

use

instead

of

its

literal

occurrence

we

would

obtain

the

wrong

kind

-RRB-

There

are

m

functions

case

t

case

t

m

that

correspond

to

the

typecases

for

each

type

-LRB-

t

a

a

i

-RRB-

-LRB-

t

m

z

z

j

-RRB-

Each

case

of

the

typecase

function

is

defined

by

providing

the

type

constructor

g

with

the

corre-ponding

types

Finally

-LRB-

a

a

i

-RRB-

-LRB-

z

z

j

-RRB-

corresponds

to

the

representations

for

the

types

-LRB-

a

a

i

-RRB-

-LRB-

z

z

j

-RRB-

The

only

difference

between

explicit

and

implicit

versions

of

the

design

pattern

for

the

typecase

component

is

that

in

the

explicit

version

the

occurrences

of

are

expanded

into

explicitly-passed

representations

of

the

form

g

a

whereas

with

the

implicit

representations

those

occurrences

are

replaced

by

type

class

constraints

of

the

form

Rep

a

The

dispatcher

can

also

be

derived

but

to

do

so

requires

extensions

to

Haskell

specifically

multiple-parameter

type

classes

with

functional

dependencies

The

problem

is

that

even

in

its

simplest

form

a

type-indexed

type

requires

at

least

two

type

arguments

the

first

one

corresponding

to

the

index

type

and

the

second

one

that

is

the

resulting

type-indexed

type

for

that

index

and

thus

depending

on

the

index

This

problem

is

not

too

serious

if

we

use

the

explicit

representations

variant

of

the

pattern

since

the

dispatcher

is

optional

but

using

implicit

representations

forces

us

outside

Haskell

class

Rep

t

d

d

n

t

d

d

n

where

rep

g

g

t

d

d

n

instance

-LRB-

a

a

i

-RRB-

Rep

-LRB-

t

a

a

i

-RRB-

d

d

where

rep

case

t

-LCB-

rep

i

-RCB-

instance

-LRB-

z

z

j

-RRB-

Rep

-LRB-

t

m

z

z

j

-RRB-

d

d

mn

m

where

rep

case

t

m

-LCB-

rep

j

-RCB-

The

type

class

Rep

has

at

least

two

type

arguments

t

and

If

there

are

parametric

types

that

depends

on

then

the

type

class

also

needs

to

account

for

those

types

-LRB-

d

d

n

-RRB-

The

class

contains

just

one

member

function

rep

used

to

build

representations

for

The

function

rep

has

a

type

class

constraint

ensuring

that

g

is

an

instance

of

There

are

at

least

m

instances

of

Rep

and

those

instances

define

rep

with

the

corresponding

case

t

function

If

we

are

implementing

an

implicit

version

of

the

design

pattern

then

the

definition

of

rep

is

complete

otherwise

for

an

explicit

version

we

need

to

apply

case

t

to

a

number

i

of

rep

functions

-LRB-

where

i

is

the

number

of

type

parameters

of

t

-RRB-

The

constraints

-LRB-

a

a

i

-RRB-

-LRB-

z

z

j

-RRB-

are

very

similar

to

the

constraints

and

in

fact

for

implicit

representations

they

coincide

they

correspond

to

representations

for

the

types

a

a

i

z

z

n

Smart

datatype

translations

Encoding

type-indexed

functions

with

smart

datatypes

proceeds

in

a

similar

fashion

to

the

encoding

with

type

classes

We

will

demonstrate

how

to

do

this

translation

using

a

GADT

syntax

-LRB-

as

found

in

the

new

GHC

Haskell

compiler

-RRB-

A

type-indexed

type

generates

a

smart

datatype

of

the

following

form

data

t

d

d

n

where

c

t

-LRB-

a

a

i

-RRB-

-LRB-

t

a

a

i

-RRB-

d

d

c

t

m

-LRB-

z

z

j

-RRB-

-LRB-

t

m

z

z

j

-RRB-

d

d

mn

m

Instead

of

being

parametrised

by

a

function

-LRB-

like

the

type

class

approach

-RRB-

a

smart

datatype

is

parametrised

by

all

the

types

on

which

it

depends

Another

difference

from

the

type

class

approach

is

that

the

functions

that

represent

each

case

are

now

replaced

by

constructors

c

t

c

t

m

that

can

just

be

pattern

matched

-LRB-

in

a

dependent

manner

-RRB-

by

functions

defined

over

those

datatypes

A

final

difference

is

that

-LRB-

a

a

i

-RRB-

-LRB-

z

z

j

-RRB-

need

to

reflect

the

fact

that

we

are

now

using

a

smart

datatype

The

changes

to

Rep

are

minimal

the

only

change

to

the

type

class

version

is

that

in

the

definition

of

rep

we

now

use

the

constructors

c

t

c

t

m

instead

of

the

functions

case

t

case

t

m

class

Rep

t

d

d

n

t

d

d

n

where

rep

t

d

d

n

instance

-LRB-

a

a

i

-RRB-

Rep

-LRB-

t

a

a

i

-RRB-

d

d

where

rep

c

t

rep

i

instance

-LRB-

z

z

j

-RRB-

Rep

-LRB-

t

m

z

z

j

-RRB-

d

d

mn

m

where

rep

c

t

m

rep

j

Tries

Tries

or

digital

search

trees

are

a

traditional

example

of

a

generic

type

Tries

make

use

of

the

structure

of

search

keys

in

order

to

organise

information

which

can

then

be

efficiently

queried

In

this

section

we

will

show

how

to

implement

generic

tries

using

a

variation

of

the

LAGP

type

representations

For

a

more

theoretical

presentation

of

tries

see

-LSB-

-RSB-

the

implementation

of

tries

presented

here

follows

closely

the

implementations

found

in

those

papers

In

-LSB-

-RSB-

the

generic

type

for

tries

is

given

as

follows

FMap

t

FMap

Unit

v

Maybe

v

FMap

Int

v

MapInt

v

FMap

Plus

t

t

v

OptPair

-LRB-

FMap

t

v

-RRB-

-LRB-

FMap

t

v

-RRB-

FMap

Prod

t

t

v

FMap

t

-LRB-

FMap

t

v

-RRB-

It

is

clear

that

the

type-indexed

function

FMap

takes

a

type

parameter

t

and

another

type

of

kind

and

returns

another

type

of

kind

Only

the

shape

of

parameter

t

is

analysed

the

other

parameter

v

needs

to

be

used

in

the

definition

because

the

resulting

type

is

parametrically

polymorphic

in

relation

to

v

We

encode

this

characterisation

of

FMap

as

follows

class

FMap

g

where

unit

g

Unit

v

Maybe

plus

g

a

v

c

g

b

v

d

g

-LRB-

Plus

a

b

-RRB-

v

-LRB-

PlusCase

c

d

-RRB-

prod

g

a

-LRB-

d

v

-RRB-

c

g

b

v

d

g

-LRB-

Prod

a

b

-RRB-

v

-LRB-

ProdCase

c

d

-RRB-

data

g

a

v

c

Iso

b

a

Iso

-LRB-

d

v

-RRB-

-LRB-

c

v

-RRB-

g

b

v

d

int

g

Int

v

MapInt

This

class

forms

the

typecase

participant

of

an

explicit

representation

variant

of

the

TypeCase

pattern

The

class

FMap

is

a

variation

of

the

Generic

class

from

Section

The

functor

g

-LRB-

-RRB-

takes

the

necessary

information

to

rebuild

the

type-indexed

type

The

three

parameters

of

the

functor

correspond

respectively

to

the

type

parameter

t

the

second

parameter

and

the

resulting

type

of

FMap

-LRB-

The

kind

of

the

resulting

type

is

now

We

could

have

used

kind

as

in

FMap

but

we

believe

this

version

is

slightly

more

readable

-RRB-

The

function

unit

just

reflects

the

change

of

the

functor

g

and

adds

the

information

for

the

parametric

type

v

and

the

functor

Maybe

that

is

used

to

define

the

trie

for

the

Unit

case

The

cases

for

plus

and

prod

have

explicit

arguments

that

correspond

to

the

recursive

calls

of

the

function

and

the

functors

PlusCase

c

d

and

ProdCase

c

d

correspond

to

the

respective

cases

of

the

type-indexed

type

The

data

function

handles

user-defined

datatypes

having

a

recursive

case

and

two

isomorphisms

the

first

between

the

structural

cases

and

a

second

between

the

tries

corresponding

to

those

cases

Finally

we

could

also

define

some

extra

base

cases

to

handle

primitive

types

such

as

Int

and

Char

The

auxiliary

definitions

for

the

newtypes

PlusCase

a

b

v

and

ProdCase

a

b

v

are

defined

as

follows

data

OptPair

a

b

Pair

a

b

newtype

PlusCase

a

b

v

PlusCase

-LCB-

unPlus

OptPair

-LRB-

a

v

-RRB-

-LRB-

b

v

-RRB-

-RCB-

newtype

ProdCase

a

b

v

ProdCase

-LCB-

unProd

a

-LRB-

b

v

-RRB-

-RCB-

The

introduction

of

OptPair

a

b

is

for

efficiency

reasons

-LSB-

-RSB-

In

order

to

use

a

user-defined

type

-LRB-

or

a

built-in

type

that

does

not

have

a

special

case

for

it

-RRB-

we

need

to

do

much

the

same

work

as

for

in

Section

As

an

example

we

show

what

to

do

for

Haskell

's

built-in

lists

list

FMap

g

g

a

-LRB-

FList

c

v

-RRB-

c

g

-LSB-

a

-RSB-

v

-LRB-

FList

c

-RRB-

list

ra

data

-LRB-

plus

unit

-LRB-

prod

ra

-LRB-

list

ra

-RRB-

-RRB-

-RRB-

listEP

-LRB-

Iso

unFList

FList

-RRB-

listEP

Iso

-LSB-

a

-RSB-

-LRB-

Plus

Unit

-LRB-

Prod

a

-LSB-

a

-RSB-

-RRB-

-RRB-

listEP

Iso

fromList

toList

where

fromList

-LSB-

-RSB-

Inl

Unit

fromList

-LRB-

x

xs

-RRB-

Inr

-LRB-

Prod

x

xs

-RRB-

toList

-LRB-

Inl

Unit

-RRB-

-LSB-

-RSB-

toList

-LRB-

Inr

-LRB-

Prod

x

xs

-RRB-

-RRB-

x

xs

newtype

FList

c

v

FList

-LCB-

unFList

-LRB-

PlusCase

Maybe

-LRB-

ProdCase

c

-LRB-

FList

c

-RRB-

-RRB-

-RRB-

v

-RCB-

The

function

list

defines

the

encoding

for

the

representation

of

lists

Because

lists

are

a

parametrised

datatype

with

one

type

parameter

list

is

a

function

that

takes

one

argument

this

argument

corresponds

to

the

representation

of

the

list

type

argument

and

list

returns

the

representation

for

lists

The

definition

is

nearly

the

same

as

the

equivalent

for

GM

but

it

takes

an

extra

isomorphism

describing

the

mapping

between

the

structural

representation

of

a

list

trie

and

a

newtype

FList

c

v

that

is

introduced

to

represent

the

resulting

list

trie

The

function

listEP

is

just

the

isomorphism

-LSB-

a

-RSB-

a

-LSB-

a

-RSB-

This

means

that

listEP

can

be

shared

with

other

versions

of

generics

that

use

the

same

structural

cases

However

list

and

FList

c

v

still

have

to

be

introduced

for

each

type-indexed

datatype

Nevertheless

that

is

boilerplate

code

and

with

compiler

support

it

is

should

be

possible

to

avoid

writing

it

Having

set

up

the

main

components

of

the

design

pattern

we

can

now

move

on

to

define

our

first

function

over

tries

The

function

empty

creates

a

new

empty

trie

and

can

be

defined

as

follows

newtype

EmptyTrie

a

v

t

EmptyTrie

-LCB-

empty

t

v

-RCB-

instance

FMap

EmptyTrie

where

unit

EmptyTrie

Nothing

int

EmptyTrie

-LRB-

MapInt

-LSB-

-RSB-

-RRB-

plus

ra

rb

EmptyTrie

-LRB-

PlusCase

-RRB-

prod

ra

rb

EmptyTrie

-LRB-

ProdCase

-LRB-

empty

ra

-RRB-

-RRB-

data

ra

iso

EmptyTrie

-LRB-

to

-LRB-

empty

ra

-RRB-

-RRB-

This

function

is

very

simple

but

nonetheless

it

has

a

type-indexed

type

the

unit

case

returns

Nothing

the

int

case

returns

a

value

of

a

user-defined

type

for

integer

tries

the

cases

for

prod

and

plus

return

respectively

values

for

the

previously

defined

ProdCase

and

PlusCase

types

finally

the

data

returns

a

value

of

the

newtype

used

to

represent

the

trie

of

some

user-defined

datatype

Another

function

that

we

will

probably

want

to

have

in

a

library

for

tries

is

the

lookUp

function

which

given

a

key

returns

the

corresponding

value

stored

in

the

trie

newtype

LUp

a

v

t

LUp

-LCB-

lookUp

a

t

v

Maybe

v

-RCB-

instance

FMap

LUp

where

unit

LUp

-LRB-

fm

fm

-RRB-

int

LUp

-LRB-

i

fm

lookUpInt

i

fm

-RRB-

plus

ra

rb

LUp

-LRB-

t

fm

case

-LRB-

unPlus

fm

-RRB-

of

Nothing

-LRB-

Pair

fma

fmb

-RRB-

case

t

of

-LRB-

Inl

l

-RRB-

lookUp

ra

l

fma

-LRB-

Inr

r

-RRB-

lookUp

rb

r

fmb

-RRB-

prod

ra

rb

LUp

-LRB-

t

-LRB-

ProdCase

fma

-RRB-

case

t

of

-LRB-

Prod

x

y

-RRB-

-LRB-

lookUp

ra

x

lookUp

rb

y

-RRB-

fma

-RRB-

data

ra

iso

LUp

-LRB-

t

r

lookUp

ra

-LRB-

from

iso

t

-RRB-

-LRB-

from

r

-RRB-

-RRB-

-LRB-

The

operator

represents

monadic

composition

-RRB-

The

functions

empty

and

lookUp

have

definitions

that

only

have

generic

function

calls

to

themselves

However

that

is

not

the

case

for

all

generic

functions

One

such

function

is

the

generic

function

that

creates

a

trie

containing

a

single

element

a

possible

definition

makes

use

of

the

generic

function

empty

We

discussed

in

Section

that

using

an

explicit

version

of

the

design

pattern

there

are

some

issues

with

generic

functions

calling

generic

functions

other

than

themselves

One

solution

for

this

problem

is

using

tupling

Just

as

one

does

with

a

type

class

we

would

choose

a

fixed

set

of

functions

and

group

them

together

in

a

record

For

instance

in

the

case

of

tries

we

could

have

the

following

data

Tries

a

v

t

Tries

-LCB-

empty

t

v

isempty

t

v

Bool

single

a

v

t

v

lookup

a

t

v

Maybe

v

insert

-LRB-

v

v

v

-RRB-

a

v

t

v

t

v

merge

-LRB-

v

v

v

-RRB-

t

v

t

v

t

v

delete

a

t

v

t

v

-RCB-

With

our

definition

we

could

for

any

function

in

the

record

make

mutual

generic

calls

Whilst

we

could

have

used

a

multiple-parameter

type

class

with

functional

dependencies

in

order

to

implement

this

library

of

functions

over

tries

there

would

be

one

important

disadvantage

in

doing

so

-LRB-

apart

from

the

fact

that

we

need

to

leave

Haskell

-RRB-

we

can

only

have

functions

on

types

of

kind

With

type

classes

contexts

are

implicitly

passed

and

there

is

no

way

to

redefine

those

implicit

behaviours

In

other

words

type

classes

have

the

same

limitation

as

implicit

representations

as

a

version

of

the

TypeCase

design

pattern

in

that

they

can

only

work

on

types

On

the

other

hand

derived

from

the

fact

that

we

use

external

representations

with

this

implementation

we

can

define

generic

functions

over

type

constructors

Tupling

is

not

the

only

option

to

solve

the

problem

of

generic

function

calls

Another

possibility

is

to

have

the

notion

of

dependencies

instead

of

tupling

all

functions

together

we

can

for

each

generic

function

that

we

need

to

use

include

one

instance

of

that

function

Here

is

a

possible

definition

of

single

using

this

strategy

data

Single

a

v

t

Single

-LCB-

emptyT

EmptyTrie

a

v

t

single

a

v

t

v

-RCB-

instance

FMap

Single

where

unit

Single

unit

-LRB-

v

Just

v

-RRB-

int

Single

int

-LRB-

i

v

MapInt

-LSB-

-LRB-

i

v

-RRB-

-RSB-

-RRB-

plus

ra

rb

Single

-LRB-

plus

-LRB-

emptyT

ra

-RRB-

-LRB-

emptyT

rb

-RRB-

-RRB-

-LRB-

i

v

case

i

of

Inl

l

PlusCase

-LRB-

Pair

-LRB-

single

ra

l

v

-RRB-

-LRB-

empty

-LRB-

emptyT

rb

-RRB-

-RRB-

-RRB-

Inr

r

PlusCase

-LRB-

Pair

-LRB-

empty

-LRB-

emptyT

ra

-RRB-

-RRB-

-LRB-

single

rb

r

v

-RRB-

-RRB-

-RRB-

prod

ra

rb

Single

-LRB-

prod

-LRB-

emptyT

ra

-RRB-

-LRB-

emptyT

rb

-RRB-

-RRB-

-LRB-

i

v

case

i

of

Prod

x

y

ProdCase

-LRB-

single

ra

x

-LRB-

single

rb

y

v

-RRB-

-RRB-

-RRB-

data

ra

iso

Single

-LRB-

data

-LRB-

emptyT

ra

-RRB-

iso

-RRB-

-LRB-

i

v

to

-LRB-

single

ra

-LRB-

from

iso

i

-RRB-

v

-RRB-

-RRB-

The

idea

of

dependencies

is

motivated

by

Dependency-Style

Generic

Haskell

-LSB-

-RSB-

In

this

version

of

Generic

Haskell

the

type

system

reflects

the

uses

of

generic

functions

in

the

definitions

by

keeping

track

of

constraints

that

identify

such

uses

With

this

definition

we

have

to

manually

introduce

those

dependencies

by

adding

extra

fields

to

the

record

that

keep

track

of

all

the

functions

on

which

the

definition

depends

That

change

is

also

reflected

in

the

instance

that

defines

the

generic

function

where

we

need

to

provide

values

for

the

extra

fields

the

values

for

those

fields

just

reconstruct

the

dependent

functions

with

their

values

for

those

fields

The

goal

of

design

patterns

is

not

to

come

up

with

a

miraculous

solution

for

a

problem

Instead

design

patterns

capture

good

techniques

that

appear

in

the

literature

or

in

practice

in

a

variety

of

contexts

and

document

them

to

make

them

easier

to

identify

and

implement

In

this

paper

we

have

generalised

the

technique

found

in

LIGD

and

GM

to

a

design

pattern

and

presented

a

number

of

applications

of

the

pattern

Furthermore

we

have

identified

other

occurrences

of

the

design

pattern

in

the

literature

Related

work

The

technique

used

by

Danvy

-LSB-

-RSB-

and

generalised

by

Yang

-LSB-

-RSB-

allows

us

to

encode

type-indexed

values

in

a

Hindley-Milner

type

system

This

encoding

is

directly

related

to

the

explicit

representation

version

of

the

TypeCase

pattern

This

technique

influenced

many

other

works

ranging

from

type-directed

partial

evaluation

-LSB-

-RSB-

through

embedded

interpreters

-LSB-

-RSB-

to

a

generalisation

of

families

of

functions

like

zipWith

-LSB-

-RSB-

these

are

all

possible

applications

of

the

TypeCase

design

pattern

Our

paper

revises

that

technique

and

shows

how

slightly

richer

type

systems

can

be

used

to

improve

it

In

particular

the

use

of

a

dispatcher

makes

it

possible

to

automatically

built

the

values

encoding

types

Moreover

the

issue

of

composability

-LRB-

identified

by

Yang

-RRB-

while

still

a

problem

can

benefit

from

stronger

type

systems

the

use

of

rank-two

types

combined

with

type

classes

provides

a

good

solution

The

work

on

extensional

polymorphism

-LSB-

-RSB-

presents

an

approach

that

allows

functions

to

implicitly

bind

the

types

of

their

arguments

in

a

modified

version

of

ML

Furthermore

using

a

typecase

construct

it

is

possible

to

support

generic

programming

Harper

and

Morrisett

's

work

on

intensional

type

analysis

-LSB-

-RSB-

presents

an

intermediate

language

where

run-time

type

analysis

is

permitted

using

typecase

and

Typecase

constructs

to

define

type-indexed

functions

and

type-indexed

types

respectively

However

approaches

based

on

run-time

type

analysis

have

important

drawbacks

for

instance

they

can

not

support

abstract

datatypes

and

they

do

not

respect

the

parametricity

theorem

-LSB-

-RSB-

Subsequent

approaches

to

intensional

type

analysis

by

Crary

and

others

-LSB-

-RSB-

use

a

type-erasure

semantics

that

does

not

suffer

from

those

problems

Still

those

approaches

were

limited

to

first-order

type

analysis

More

recently

Weirich

-LSB-

-RSB-

proposed

a

version

of

intensional

type

analysis

covering

higher-order

types

with

a

type-erasure

semantics

Furthermore

she

presented

an

implementation

in

Haskell

-LRB-

augmented

with

rank-two

types

-RRB-

This

work

inspired

Hinze

's

implementation

of

GM

which

shows

in

essence

how

to

avoid

rank-two

types

by

using

Haskell

's

class

system

Our

work

makes

use

of

those

results

and

explains

how

to

simulate

typecase

constructs

Furthermore

we

show

that

the

limitation

of

GM

that

generic

functions

with

generic

types

can

not

be

defined

can

be

lifted

with

our

more

general

interpretation

Generic

programming

-LRB-

or

perhaps

datatype-generic

programming

-LSB-

-RSB-

-RRB-

is

about

defining

functions

and

types

that

depend

on

the

structure

of

other

types

One

of

the

first

attempts

to

produce

a

generic

programming

language

was

PolyP

-LSB-

-RSB-

This

language

allowed

the

definition

of

generic

functions

over

regular

datatypes

with

one

type

parameter

In

Section

we

show

that

using

our

design

pattern

it

is

possible

to

define

PolyP-like

generic

functions

just

using

Haskell

A

previous

attempt

-LSB-

-RSB-

to

define

first-class

PolyP

functions

in

Haskell

required

extensions

to

the

language

The

Generic

Haskell

-LSB-

-RSB-

project

is

more

ambitious

than

PolyP

and

aims

at

defining

generic

functions

for

nearly

all

types

defin-able

in

Haskell

Furthermore

Generic

Haskell

features

generic

types

and

generic

function

customisation

-LRB-

which

were

not

present

in

PolyP

-RRB-

Dependency-Style

Generic

Haskell

-LSB-

-RSB-

introduces

a

rather

complex

type

system

that

keeps

track

of

dependencies

on

generic

function

calls

The

need

for

this

sophisticated

type

system

is

a

consequence

of

a

model

for

generic

programming

that

allows

generic

function

customisation

The

approach

presented

in

-LSB-

-RSB-

is

another

kind

of

lightweight

approach

to

generic

programming

relying

on

a

run-time

type-safe

cast

operator

With

that

operator

it

is

possible

to

define

a

number

of

traversals

that

allow

a

very

interesting

model

of

generic

programming

based

on

nominal

typing

Our

design

pattern

can

be

used

to

encode

many

of

the

generic

definitions

that

these

generic

programming

techniques

allow

However

it

can

be

less

practical

than

approaches

providing

a

special-purpose

compiler

Nevertheless

the

advantage

of

our

technique

is

that

we

do

not

need

to

commit

in

advance

to

a

model

of

generic

programming

we

have

the

freedom

to

choose

our

own

model

of

generic

programming

Design

patterns

in

the

object-oriented

programming

community

have

been

given

a

great

deal

of

attention

Whilst

amongst

the

functional

programming

community

there

has

been

some

work

on

or

at

least

involving

the

concept

of

design

patterns

-LSB-

-RSB-

the

concept

is

still

much

less

popular

than

in

the

object-oriented

community

Moreover

most

of

this

work

presents

patterns

that

are

really

more

like

algorithmic

patterns

rather

than

design

patterns

Perhaps

the

reason

why

this

happens

is

that

functional

languages

are

very

expressive

and

often

natural

features

of

those

languages

such

as

laziness

or

higher-order

functions

can

be

used

to

remove

the

need

for

complex

designs

Nevertheless

we

believe

that

our

design

pattern

is

more

related

to

the

OO

concept

of

a

design

pattern

with

type

classes/datatypes

taking

the

role

of

OO

interfaces

and

class

instances

taking

the

role

of

OO

concrete

classes

One

difficulty

found

in

this

work

had

to

do

with

the

fact

that

unlike

OO

design

patterns

which

are

documented

using

informal

notations

such

as

UML

we

do

not

have

a

notation

to

talk

about

the

design

of

Haskell

programs

The

notation

that

we

used

is

quite

ad-hoc

and

it

can

be

difficult

to

read

Future

work

We

mentioned

that

this

design

pattern

seems

to

be

very

similar

to

OO

design

patterns

It

would

be

interesting

to

explore

the

applicability

of

this

design

pattern

in

an

OO

environment

Design

patterns

are

useful

to

overcome

the

lack

of

certain

features

in

programming

languages

In

our

case

we

overcome

the

lack

of

a

typecase

construct

The

work

on

intensional

type

analysis

investigates

the

possibility

of

languages

supporting

typecase

constructs

directly

in

the

language

Combining

these

results

in

order

to

extend

Haskell

with

a

more

natural

support

for

typecase

programming

is

something

we

would

like

to

try

in

the

future

Problems

that

use

multiple

instances

of

the

design

pattern

are

not

composable

For

instance

in

a

generic

programming

context

we

could

have

a

class

Generic

that

allowed

us

to

define

generic

functions

with

one

type

parameter

and

we

could

also

have

a

class

FMap

for

working

with

tries

Although

those

classes

are

structured

in

a

similar

way

they

require

two

distinct

representations

of

types

one

for

each

of

the

classes

we

hope

to

address

this

impracticality

Acknowledgements

We

would

like

to

thank

Ralf

Hinze

for

the

discussion

that

inspired

this

paper

Stefan

Holdermans

the

anonymous

referees

and

the

members

of

the

Algebra

of

Programming

group

at

Oxford

and

the

EPSRC-funded

Datatype-Generic

Programming

project

made

a

number

of

helpful

suggestions

-LSB-

-RSB-

C.

Alexander

A

Pattern

Language

Oxford

University

Press

-LSB-

-RSB-

N.

Benton

Embedded

interpreters

Microsoft

Research

Cambridge

Jan.

-LSB-

-RSB-

R.

Bird

and

O.

de

Moor

Algebra

of

Programming

International

Series

in

Computer

Science

Prentice

Hall

-LSB-

-RSB-

J.

Cheney

and

R.

Hinze

A

lightweight

implementation

of

generics

and

dynamics

In

Haskell

Workshop

pages

-LSB-

-RSB-

D.

Clarke

and

A.

L

h.

Generic

Haskell

specifically

In

Generic

Programming

pages

Kluwer

B.V.

-LSB-

-RSB-

K.

Crary

and

S.

Weirich

Flexible

type

analysis

In

International

Conference

on

Functional

Programming

pages

-LSB-

-RSB-

K.

Crary

S.

Weirich

and

J.

G.

Morrisett

Intensional

polymorphism

in

type-erasure

semantics

In

International

Conference

on

Functional

Programming

pages

-LSB-

-RSB-

N.

A.

Danielsson

and

P.

Jansson

Chasing

bottoms

A

case

study

in

program

verification

in

the

presence

of

partial

and

infinite

values

In

D.

Kozen

editor

LNCS

Mathematics

of

Program

Construction

pages

Springer-Verlag

-LSB-

-RSB-

O.

Danvy

Type-directed

partial

evaluation

In

Principles

of

Programming

Languages

-LSB-

-RSB-

O.

Danvy

Functional

unparsing

Journal

of

Functional

Programming

-LRB-

-RRB-

-LSB-

-RSB-

C.

Dubois

F.

Rouaix

and

P.

Weis

Extensional

polymorphism

In

Principles

of

Programming

Languages

pages

-LSB-

-RSB-

P.

Dybjer

and

A.

Filinski

Normalization

and

partial

evaluation

In

LNCS

Applied

Semantics

pages

Springer

-LSB-

-RSB-

D.

Fridlender

and

M.

Indrika

Do

we

need

dependent

types

Journal

of

Functional

Programming

-LRB-

-RRB-

-LSB-

-RSB-

J.

Gibbons

Calculating

functional

programs

In

Algebraic

and

Coalgebraic

Methods

in

the

Mathematics

of

Program

Construction

pages

-LSB-

-RSB-

J.

Gibbons

Patterns

in

datatype-generic

programming

In

Declarative

Programming

in

the

Context

of

Object-Oriented

Languages

-LSB-

-RSB-

R.

Harper

and

G.

Morrisett

Compiling

polymorphism

using

intensional

type

analysis

In

Principles

of

Programming

Languages

pages

San

Francisco

California

-LSB-

-RSB-

R.

Hinze

Generalizing

generalized

tries

Journal

of

Functional

Programming

-LRB-

-RRB-

-LSB-

-RSB-

R.

Hinze

Fun

with

phantom

types

In

J.

Gibbons

and

O.

de

Moor

editors

The

Fun

of

Programming

pages

Palgrave

-LSB-

-RSB-

R.

Hinze

Generics

for

the

masses

In

International

Conference

on

Functional

Programming

pages

ACM

Press

-LSB-

-RSB-

R.

Hinze

J.

Jeuring

and

A.

L

h.

Type-indexed

data

types

Science

of

Computer

Programming

-LRB-

-RRB-

-LSB-

-RSB-

P.

Jansson

Functional

Polytypic

Programming

PhD

thesis

Chalmers

University

of

Technology

May

-LSB-

-RSB-

J.

Jeuring

and

P.

Jansson

Polytypic

programming

In

J.

Launchbury

E.

Meijer

and

T.

Sheard

editors

LNCS

Advanced

Functional

Programming

pages

Springer-Verlag

-LSB-

-RSB-

T.

K

hne

A

Functional

Pattern

System

for

Object-Oriented

Design

Verlag

Dr.

Kovac

ISBN

Hamburg

Germany

-LSB-

-RSB-

R.

L

mmel

and

S.

Peyton

Jones

Scrap

your

boilerplate

a

practical

design

pattern

for

generic

programming

In

Types

in

Language

Design

and

Implementation

-LSB-

-RSB-

R.

L

mmel

and

J.

Visser

Design

patterns

for

functional

strategic

programming

In

Workshop

on

Rule-Based

Programming

-LSB-

-RSB-

A.

L

h.

Exploring

Generic

Haskell

PhD

thesis

Utrecht

University

-LSB-

-RSB-

A.

L

h

D.

Clarke

and

J.

Jeuring

Dependency-style

Generic

Haskell

In

International

Conference

on

Functional

Programming

pages

-LSB-

-RSB-

E.

Meijer

M.

Fokkinga

and

R.

Paterson

Functional

programming

with

bananas

lenses

envelopes

and

barbed

wire

In

LNCS

Functional

Programming

Languages

and

Computer

Architecture

pages

Springer-Verlag

-LSB-

-RSB-

D.

Menendez

Fixed-length

vectors

in

Haskell

-LSB-

-RSB-

U.

Norell

and

P.

Jansson

Polytypic

programming

in

Haskell

In

Implementing

Functional

Languages

-LSB-

-RSB-

S.

Peyton

Jones

editor

Haskell

Language

and

Libraries

The

Revised

Report

Cambridge

University

Press

-LSB-

-RSB-

S.

Peyton

Jones

G.

Washburn

and

S.

Weirich

Wobbly

types

Type

inference

for

generalised

algebraic

data

types

Microsoft

Research

Cambridge

-LSB-

-RSB-

J.

C.

Reynolds

Types

abstraction

and

parametric

polymorphism

In

Information

Processing

pages

Elsevier

-LSB-

-RSB-

P.

Wadler

Views

a

way

for

pattern

matching

to

cohabit

with

data

abstraction

In

Principles

of

Programming

Languages

pages

ACM

Press

-LSB-

-RSB-

P.

Wadler

Theorems

for

free

In

Functional

Programming

and

Computer

Architecture

-LSB-

-RSB-

S.

Weirich

Higher-order

intensional

type

analysis

in

type-erasure

semantics

-LSB-

-RSB-

Z.

Yang

Encoding

types

in

ML-like

languages

In

International

Conference

on

Functional

Programming

pages