Getting Haskell to Distinguish Type Synonyms

115 Views Asked by At

I like the idea of Haskell type synonyms, because they allow for distinguishing between abstract datatypes that share underlying representations. Unfortunately, when I write a program like

data Vector a = Vec a a

-- Some definitions here about (+) and (*) for Vector ...

type Position = Vector Float
type Velocity = Vector Float
type Time = Float

step :: Position -> Velocity -> Time -> Position
step p v dt = p + v*dt

p :: Position
p = Vec 0.0 0.0

v :: Velocity
v = Vec 1.0 1.0

p' = step v p 0.01

This is perfectly valid Haskell code, despite v and p being in the wrong spots. I would like to strengthen the distinction between type synonyms, such that they still share underlying representation, but are not accepted as each other in function application. Is this possible?

2

There are 2 best solutions below

2
Aadit M Shah On BEST ANSWER

You could make Vector a phantom type as follows:

data Vector t a = Vec a a

data Pos
data Vel

type Position = Vector Pos Float
type Velocity = Vector Vel Float

Now, you can define instances of Position and Velocity like you'd normally do:

p :: Position
p = Vec 0.0 0.0

v :: Velocity
v = Vec 1.0 1.0

However, it won't allow you to use them interchangeably:

type Time = Float

step :: Position -> Velocity -> Time -> Position
step p v dt = p + v*dt -- you might have to change this definition

p' = step v p 0.01 -- won't compile

You can also make things more precise by using DataKinds and KindSignatures:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}

data VectorType = Pos | Vel

data Vector (t :: VectorType) a = Vec a a

type Position = Vector Pos Float
type Velocity = Vector Vel Float

Hope that helps.

0
bergey On

newtype is likely what you want, or at any rate, the best we've got. Like type, it defines a new name for an existing type, and the runtime representation will be the same. Unlike type (but like data), they are considered different during type checking, and there is a new data constructor.

So you might have code like:

newtype Position = Position (Vector Float)
p :: Position
p = Position (Vec 0 0)