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?
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.