7

考虑以下

data Predicate = Pred Name Arity Arguments

type Name      = String
type Arity     = Int
type Arguments = [Entity]
type Entity    = String

这将允许创建

Pred "divides" 2 ["1", "2"]
Pred "between" 3 ["2", "1", "3"]

也是“非法”

Pred "divides" 2 ["1"]
Pred "between" 3 ["2", "3"]

“非法”,因为元数与参数列表的长度不匹配。

缺少使用这样的功能

makePred :: Name -> Arity -> Arguments -> Maybe Predicate
makePred n a args | a == length args = Just (Pred n a args)
                  | otherwise = Nothing

并且只从 Predicate 模块导出 makePred ,有没有办法强制值构造函数的正确性?

4

2 回答 2

6

好吧,简单的答案是从智能构造函数中删除arity。

makePred :: Name -> Arguments -> Predicate
makePred name args = Pred name (length args) args

然后,如果您不Pred从模块中公开构造函数并强制您的客户端通过makePred,您就会知道它们将始终匹配,并且您不需要那些难看Maybe的 .

没有直接的方法来强制执行该不变量。也就是说,您将无法进行类型makePred 2 ["a","b"]检查,但makePred 2 ["a","b","c"]不能。为此,您需要真正的依赖类型。

中间有一些地方可以说服 haskell 使用高级功能(GADTs + 幻像类型)强制执行您的不变量,但是在写出完整的解决方案后,我意识到我并没有真正解决您的问题,而且这些技术并不真正适用尤其是这个问题。它们通常也比它们的价值更麻烦。我会坚持使用智能构造函数。

我已经对智能构造函数的想法进行了深入的描述。事实证明,它是类型验证和运行时验证之间的一个非常令人愉快的中间地带。

于 2011-01-27T08:21:29.123 回答
0

在我看来,如果您希望所说的限制是可执行的,那么您应该创建Predicate一个类,并且每种谓词都有自己的数据类型,即Predicate.

这将使您有可能在谓词中使用 String 类型以外的参数。

像这样的东西(未经测试)

data Entity = Str String | Numeric Int

class Predicate a where
    name :: a -> String
    arity :: a -> Int
    args :: a -> [Entity]
    satisfied :: a -> Bool

data Divides = Divides Int Int
instance Predicate Divides where
    name p = "divides"
    arity p = 2
    args (Divides n x) = [(Numeric n), (Numeric x)]
    satisfied (Divides n x) = x `mod` n == 0

这可能会也可能不会解决您的问题,但它肯定是一个值得考虑的强大选择。

于 2011-01-26T23:44:02.983 回答