McBear Holden
McBear Holden

Reputation: 833

How to enforce type relation and constraints when defing data type

In the following code:

data Point = Point { x           :: Int, 
                     leftHeight  :: Int, 
                     rightHeight :: Int}
data Rectangle = Rec Point Point

rec1 = Rec p1 p2

There are several constraints when creating Rec such as:

x p1 < x p2
(leftHeight p1 ) should always be zero
(rightHeight p2 ) should always be zero
rightHeight p1 == leftHeight p2

Is there a way to enforce these constraints at type level? Because I'm using QuickCheck to generate some sample Rec but these contraints make QuickCheck very slow to generate random samples.

edit: I've solved the slow QuickCheck issue. But anyway still curious if such constraint can be expressed in Haskell

Upvotes: 1

Views: 89

Answers (1)

K. A. Buhr
K. A. Buhr

Reputation: 50864

To point out the obvious, the standard way of enforcing these constraints at type level would be to define your Rectangle differently:

data Rectangle = Rec
  { x1 :: Int
  , x2 :: Int
  , height :: Int
  }

This enforces all constraints except "x1 < x2". You can use smart destructors to recreate the rectangle's Point "fields":

point1 :: Rectangle -> Point
point1 (Rec x1 _ h) = Point x1 0 h

point2 :: Rectangle -> Point
point2 (Rec _ x2 h) = Point x2 h 0

and define a smart constructor (which can also enforce the "x1 < x2" constraint):

rect :: Point -> Point -> Rectangle
rect (Point x1 0 h1) (Point x2 h2 0) | h1 == h2 && x1 < x2 = Rec x1 x2 h1
rect _ _ = error "bad rectangle"

Seriously, this really is the best way to enforce constraints at the type level in Haskell!

Upvotes: 3

Related Questions