Associated Type Synonyms in Haskell


Post explores the basic idea behind Type Synonyms in Haskell

Type families in Haskell enable type resolution based on other types. Type families mirror typeclasses (which resolve function implementations based on types). Let’s write two records - a person and an animal:

data Person = Person { name :: String} deriving Show
data Animal = Animal { species :: String} deriving Show

aPerson :: Person
aPerson = Person { name = "Jan" }

anAnimal :: Animal
anAnimal = Animal { species = "dog" }

Let’s now suppose that each record lives in a different environment - an Animal is always in a Wilderness and a Person is in a House,

data House = House Person deriving Show
data Wilderness = Wilderness Animal deriving Show

We now come to the crux of the matter. We want to write a function env which places each entity into the right environment - a Person into the House and an Animal into the Wilderness. How do we do that? Let’s solve a simpler problem first - let’s write env which places a Person into a House, that is the argument of env is a Person and the output is the proper environment for the person (i.e. a House).

env :: Person -> House
env p = House p

print $ env aPerson

Which gives us the output House (Person {name = "Jan"}). But now we want to extend the env function such that we can call print $ env anAnimal. This can be done by a typeclass, but what would be the type signature of env?

class Environment a where
env :: a -> ???

This is where type families come in. We can use an associated type synonym (we can call it Env) and define the return type of env in the instances of the typeclass.

class Environment a where
type Env a :: *
env :: a -> Env a

This definition says that we have a associated type synonym Env a where the result type is chosen according to the type variable a. All we need to do now is to write the instances:

instance Environment Person where
type Env Person = House
env = House

instance Environment Animal where
type Env Animal = Wilderness
env = Wilderness

For each instance we define the type synonym. For example Env Person = House Person in the first instance and Env Animal = Wilderness Animal in the second instance. We can test the implementation by printing the following:

main = do
print $ env anAnimal
print $ env aPerson

We get:

"Wilderness (Animal {species = "Dog"})"
"House (Person {name = "Jan"})"