Type system for constructor preconditions

I'm interested in forcing data passed to a constructor to meet certain preconditions. I've seen the wiki entry on smart constructors, and would have stopped thinking about the problem if hiding the data constructors didn't also make it impossible to do pattern matching. Ideally, the type system could do these checks, but we don't have dependent types so I kept looking. The pattern below uses the type system to force a client of the Circle data type to pass in valid (positive) radius. This isn't quite what is on the smart constructors wiki page. Does pattern have a name? Where can I read more about it? What are the negatives? The positives are that when a client goes to create a Circle in their code, they'll notice that they need to pass a ValidRadius, not just any number, so then they'll *have* to find a function that gives them a ValidRadius, and thus will be forced to be semi-informed about what sorts of preconditions exist on the Circle constructor; They'll have to deal with the case that validRadius gives them Nothing. If we had simply put a comment by the Circle constructor to use a validateRadius function to make sure their number is valid, they could have ignored it. -- In Circle.hs module Circle ( Circle (Circle), Radius, validRadius, ValidRadius ) where type Radius = Int data ValidRadius = ValidRadius Radius deriving (Eq, Show) data Circle = Circle ValidRadius deriving (Eq, Show) -- A radius must be positive. validRadius :: Radius -> Maybe ValidRadius validRadius x | x > 0 = Just $ ValidRadius x | otherwise = Nothing -- In Client.hs import Circle r = 3 :: Radius vr :: ValidRadius vr = case validRadius r of Nothing -> error "Tell UI about bad input" Just x -> x

Hi Bryan, On Wed, Jan 16, 2013 at 03:10:18PM -0800, Bryan Vicknair wrote:
Ideally, the type system could do these checks, but we don't have dependent types so I kept looking. The pattern below uses the type system to force a client of the Circle data type to pass in valid (positive) radius. This isn't quite what is on the smart constructors wiki page. Does pattern have a name? Where can I read more about it? What are the negatives?
It's just a smart constructor. Instead of using one for Circle, you are now using one for ValidRadius. You still have the same pattern matching issue, because how would you access the radius inside of ValidRadius? But instead of using pattern matching to access the fields of Circle you could offer accessor functions. module Circle (Circle, Radius, circle, radius) where type Radius = Int data Circle = Circle Radius deriving (Eq, Show) circle :: Radius -> Maybe Circle circle r | r > 0 = Just $ Circle r | otherwise = Nothing radius :: Circle -> Radius radius (Circle r) = r Greetings, Daniel
participants (2)
-
Bryan Vicknair
-
Daniel Trstenjak