如何设置约束或验证/条件验证(对类型变量)?这里:如何强制半径> 0?

时间:2012-07-20 13:49:43

标签: haskell types typeclass

如果必须设置约束,则创建的任何圆的半径必须大于零(半径> 0)。怎么做?

data Point = Point Float Float deriving (Show)  
data Radius = Radius Float deriving (Show)  
data Shape = Circle Point Radius deriving (Show)
surface :: Shape -> Float  
surface (Circle _ (Radius r)) = pi * r ^ 2  

如果方便,请提供更多示例,说明如何在各种情况下设置约束/验证。例如。数据电话可以有正则表达式或特定的起始编号(区号或国家代码等)。

2 个答案:

答案 0 :(得分:6)

在数据类型字段上实现验证的最简单方法是不从模块中导出值构造函数,而是定义和导出在使用隐藏值构造函数实际构造和返回对象之前执行所需检查的函数。

一个简单的示例,有两种可能的方法来报告错误:

module MyModule
( Radius  -- we do not export value constructors
, radius
, radius'
) where

data Radius = Radius Float deriving (Show)

radius :: Float -> Maybe Radius
radius r | r > 0     = Just (Radius r)
         | otherwise = Nothing

radius' :: Float -> Radius
radius' r | r > 0     = Radius r
          | otherwise = error "negative radius"

通过这种方式,模块的用户只能通过您个人定义的函数创建新值,而不能通过值构造函数创建新值,这将使他们能够跳过所有检查。

答案 1 :(得分:3)

如果你想要比Riccardo的解决方案更有趣的东西,你可以使用镜头作为你的类型的接口。您现在可以使用fclabels执行此操作,但是无法区分外部构造函数上的失败与您尝试验证的内部值的失败。

我还写了一个实验lens lib试图以更有条理的方式解决该用例,但我不能在这个阶段推荐它。