1

如果必须设置约束,则创建的任何 Circle 的 Radius 必须大于零( Radius > 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  

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

4

2 回答 2

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"

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

于 2012-07-20T13:52:22.633 回答
3

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

我还编写了一个实验性镜头库,试图以更结构化的方式解决该用例,但在这个阶段我真的不推荐它。

于 2012-07-20T14:18:03.683 回答