Testing 使用Haskell';s类型以替换断言语句,或者如果使用其他语言进行检查

Testing 使用Haskell';s类型以替换断言语句,或者如果使用其他语言进行检查,testing,haskell,types,type-systems,Testing,Haskell,Types,Type Systems,抱歉,如果问题很简单,我对哈斯克尔还是很陌生的。假设我有一个函数,它只能处理两个黄金比例(1.618)中的数字,我如何定义myfunxy的类型以只取黄金比例数字。如果我在程序中调用myfun时没有黄金比例数字(编译错误?),会发生什么?如果在运行时通过用户输入进行不带黄金分割数的调用,会发生什么情况?实际上,您最好进行运行时检查。可能有一些我不知道的类型级演算(见luqui的评论),但这在Haskell中并不实用 您可以使用,这是您要替换的 checker :: a -> b ->

抱歉,如果问题很简单,我对哈斯克尔还是很陌生的。假设我有一个函数,它只能处理两个黄金比例(1.618)中的数字,我如何定义myfunxy的类型以只取黄金比例数字。如果我在程序中调用myfun时没有黄金比例数字(编译错误?),会发生什么?如果在运行时通过用户输入进行不带黄金分割数的调用,会发生什么情况?

实际上,您最好进行运行时检查。可能有一些我不知道的类型级演算(见luqui的评论),但这在Haskell中并不实用

您可以使用,这是您要替换的

checker :: a -> b -> Bool
checker x y = x * 1.618 `approxEqual` y

unsafeMyfun :: a -> b -> c
unsafeMyfun x y = assert (checker x y) (doRealThingWith a b)
或者返回一个
或者一个
(或者
或者err a
),以避免在纯函数中无法捕获的异常

myfun :: a -> b -> Maybe c
myfun x y = do
              guard $ checker x y
              return $ doRealThingWith x y
或者使用Tom的答案等中的自定义契约类型。无论如何,都不可能在编译时检查约束。事实上,由于IO monad,任何编译时约束都不可能精确。

您可能需要一个只能用黄金比例数字构造的数据类型,然后编写myfun以接受该数据类型

我假设Integer是一个基类型,但您可以使用其他类型(例如:Double或Float),甚至可以是多态的

1) 做ADT

module Golden (Gold, getGold, buildGold) where

data Gold = G Integer Integer

getGold :: Gold -> (Integer, Integer)
getGold (G x y) = (x, y)

buildGold :: Integer -> Integer -> Maybe Gold
buildGold x y
    | isGolden x y = Just (G x y)
    | otherwise    = Nothing
请注意,此模块导出的是
Gold
类型,而不是构造函数(即,不是
G
)。因此,获取类型为
Gold
的值的唯一方法是使用
buildGold
,它执行运行时检查(但只有一个),因此所有消费者都可以使用Gold值,并假定其为黄金比率,而无需进行检查

2) 使用ADT构建
myfun

myfun :: Gold -> ???
myfun g = expr
  where (x, y) = getGold g
现在,如果您尝试使用非金色数字(不是
Gold
类型的值)调用
myfun
,那么您将得到一个编译时错误

重述 要生成黄金数字,必须使用函数
buildGold
,该函数强制检查数字

注意什么时候被检查!您有一个编译时保证,
myfun
,以及您希望与
Gold
一起使用的所有其他函数,都始终提供黄金比例。程序输入(来自用户、网络或任何地方)仍然需要运行时检查,这就是
buildGold
提供的;显然,永远不会有一个程序能保证人类不会键入不受欢迎的内容


对你的问题的评论中给出的备选方案也值得考虑。如果您只需要一个函数,
myfun
,它可能会失败,那么ADT的重量会稍重一些,然后只需使用
myfun::(整数,整数)->可能???
最简单的方法是使用一个从Int到GoldenInt的函数来检查您的值是否符合要求的比率

通过更多的努力,您可以使用来确保不需要运行时检查,但是,鉴于您是初学者,我将坚持使用智能构造函数方法


Tom在上面的回答就是这个习惯用法的一个例子。

那么为什么需要两个参数呢?你不能让函数取一个参数,并假设另一个参数大1.618倍吗?你不能在编译时强制执行这一点(使用两个参数)——正如你所指出的,也不能防止用户输入。这里是一个反挑战,给定两个数字x和y,当它们形成黄金比例时,写一个函数返回真值。现在,根据需要修改它以返回一些黄金比率或什么都不返回。不要陷入一个糟糕的例子中。如果它有助于想象x和y是一条消息和一个咸散列。我只想在x的salt散列与y匹配时执行myfun。假设我还有10个函数也需要这个不变量,类型系统能保证吗?下面是一个功能设计模式,它可能有您想要的折衷:您可以使用依赖类型的总体语言(如Agda或Coq)完全进行类型验证,但在他们目前的技术水平下,他们仍然很难编程。不-他可以通过使用类型系统来做得更好,以确保在输入到
myfun
之前检查所有微弱的金色数字。而且最好永远不要比较两个浮点数的精确等价性。@EFraim:正如OP所评论的,“不要太沉迷于一个坏例子。如果它有助于想象x和y是一条消息和咸杂凑。。。“,所以我们不要太关注它。@KennyTM:我同意我们不应该太关注它,但是用正确的方法比较浮点数要求不高。我从投票结果来看,有些人不同意这一点,但这是真的,正如Kenny所说,IO monad证明了这一点:
inputA您应该导出getGold和buildGold,并且您应该在不使用其构造函数的情况下导出Gold。
data Gold=G Double Double
如果您想使用固定类型;你永远找不到两个满足黄金比例的
Integer
s。是的,我应该把它们导出-编辑
Gold
是在没有构造函数的情况下导出的,因此除非您需要不必要的显式
()
,否则我不确定您想要什么。肯尼:很明显,这并没有运行,因为有一些省略的函数供询问者与myfun一起填写,而不是实际在模块中或导入任何内容。ADT的概念很重要。@Tom:假设
myfun g=a+b
isGolden x y=x==y
。我感兴趣的是如何在编译时将一个
可能是Gold
转换成
Gold
。rampion,Paul:我知道两个整数不能完全是一个黄金比率,任何n位数字(例如:双精度或浮点)也不能。这就是为什么你没有
a/b==golden
,而是需要某种近似等式或布尔测试,我称之为“isGolden”。类型算术和智能构造函数链接的结尾非常聪明(这是我大一以来看到的第一段代码)