如何向Haskell数据构造函数添加输入检查?假设我有
import Data.Time.Calendar
data SchedulePeriod = SchedulePeriod { startDate :: Day
, endDate :: Day
, accrualStart :: Day
, accrualEnd :: Day
, resetDate :: Day
, paymentDate :: Day
, fraction :: Double }
deriving (Show)
我想强加一个约束startDate < endDate
。有没有办法在不创建抽象数据类型的情况下完成它?
答案 0 :(得分:17)
标准方法是使用smart constructor在创建值之前检查前置条件,并且不导出它使用的实际构造函数。当然,正如你所说,这是创建一个抽象数据类型。
在没有智能构造函数的情况下实现此目的的唯一方法是真的邪恶类型系统hackery(并且您将无法使用标准Day
类型)。
答案 1 :(得分:13)
接受ehird的回答。我只是写这个,所以我可以解释我在评论中提到的智能析构函数,我不能在评论中说明这些解释。
假设你有类型:
data T x y z = A | B x | C y z
为了解释这一点,让我们首先介绍如果构造函数被暴露,我们如何编写类型为T的函数:
myFunction :: T x y z -> d
myFunction t = case t of
A -> f1
B x -> f2 x
C y z -> f3 y z
我们从函数的类型签名中知道f1
,f2
和f3
的类型必须是:
f1 :: d
f2 :: x -> d
f3 :: y -> z -> d
因此,如果我们将myFunction
概括为智能析构函数,我们只需将f1
,f2
和f3
作为参数传递给它:
smartDestructor :: d -> (x -> d) -> (y -> z -> d) -> t -> d
smartDestructor f1 f2 f3 t = case t of
A -> f1
B x -> f2 x
C y z -> f3 y z
因此,如果您导出smartDestructor
,那么人们基本上可以根据您的类型进行模式匹配,而无需访问构造函数。
如果您曾经使用过maybe
或either
函数,那么您已经使用了智能析构函数,尽管在这些情况下构造函数不是隐藏的,因此它们主要是作为便利功能:
maybe :: b -> (a -> b) -> Maybe a -> b
maybe f1 f2 m = case m of
Nothing -> f1
Just a -> f2 x
either :: (a -> c) -> (b -> c) -> Either a b -> c
either f1 f2 e = case e of
Left a -> f1 a
Right b -> f2 b
在您的情况下,智能析构函数的目的只是为了隐藏构造函数而不暴露构造函数。