简体   繁体   English

Haskell数据构造函数中的输入检查

[英]Input checks in Haskell data constructors

How can I add input checks to Haskell data constructors? 如何将输入检查添加到Haskell数据构造函数? Let's say I have 假设我有

import Data.Time.Calendar

data SchedulePeriod = SchedulePeriod { startDate :: Day
    , endDate :: Day
    , accrualStart :: Day
    , accrualEnd :: Day
    , resetDate :: Day
    , paymentDate :: Day
    , fraction :: Double }
    deriving (Show)

and I want to impose a constraint startDate < endDate . 我想施加一个约束startDate < endDate Is there a way to do it without creating an abstract data type? 有没有一种方法可以创建抽象数据类型呢?

The standard way is to use a smart constructor that checks the precondition before creating the value, and to not export the real constructor it uses. 标准方法是使用智能构造函数在创建值之前检查先决条件,而不导出其使用的实际构造函数。 Of course, this is creating an abstract data type, as you said. 当然,正如您所说,这是在创建抽象数据类型。

The only way to achieve this without a smart constructor would be really evil type-system hackery (and you wouldn't be able to use the standard Day type). 在没有智能构造函数的情况下实现此目标的唯一方法将是真正的邪恶的类型系统黑客(并且您将无法使用标准的Day类型)。

Accept ehird's answer. 接受ehird的答案。 I'm just writing this so I can explain the smart destructors I mentioned in a comment and I can't fit the explanation in a comment. 我只是在写这篇文章,所以我可以解释我在评论中提到的智能析构函数,而我不适合在评论中进行解释。

Let's say that you have the type: 假设您具有以下类型:

data T x y z = A | B x | C y z

ehird already explained how to abstract away the constructor, which is just to provide "smart" constructors. ehird已经解释了如何抽象出构造函数,这只是为了提供“智能”构造函数。 Like you mentioned, this requires hiding the constructors and then you can't use them to pattern match. 就像您提到的那样,这需要隐藏构造函数,然后才能使用它们进行模式匹配。 However, you can solve this using a "smart" destructor, which is equivalent to pattern-matching against every possible constructor. 但是,您可以使用“智能”析构函数来解决此问题,这等效于针对每个可能的构造函数进行模式匹配。

To explain this, let's first start with how we'd write a function of type T if the constructors were exposed: 为了解释这一点,让我们首先从如果暴露构造函数的情况下如何编写类型为T的函数开始:

myFunction :: T x y z -> d
myFunction t = case t of
    A     -> f1
    B x   -> f2 x
    C y z -> f3 y z

We know from the function's type signature that the types of f1 , f2 , and f3 must be: 从函数的类型签名我们知道f1f2f3的类型必须为:

f1 :: d
f2 :: x -> d
f3 :: y -> z -> d

So if we were to generalize myFunction to be a smart destructor, we just pass f1 , f2 , and f3 as parameters to it: 因此,如果要将myFunction泛化为智能析构函数,我们只需将f1f2f3作为参数传递给它:

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

So if you export smartDestructor , then people can basically pattern-match against your type without needing access to the constructors. 因此,如果导出smartDestructor ,那么人们基本上可以根据您的类型进行模式匹配,而无需访问构造函数。

If you've ever used the maybe or either functions, before, then you've used a smart destructor, although in those cases the constructors are not hidden, so they are mainly provided as convenience functions: 如果您以前曾经使用过maybeeither函数,那么您曾经使用过智能析构函数,尽管在这种情况下,构造函数没有被隐藏,因此它们主要作为便捷函数提供:

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

In your case, the purpose of the smart destructor is just so you can hide the constructors and not expose the constructors. 在您的情况下,智能析构函数的目的只是为了使您可以隐藏构造函数而不公开构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM