大致上,这就是类型类的工作方式。一个人可以宣布
class C a where
foo :: a -> Bool
然后
instance C (Int -> Bool) where
foo f = f 42
instance C (String -> Bool) where
foo f = f "hello"
instance C (String -> [Int]) where
foo f = sum (f "hello") > 42
以此类推。
这显然可以使foo
“检测”其自变量f
的类型并采取相应的措施。实际上,发生的事情是Haskell执行类型推断,在此期间在编译时选择了适当的实例。在运行时,不会发生“类型检测”;实际上,类型在编译后会被擦除,并且在运行时没有类型信息可用,因此将无法检测到f
属于哪个类型。
当然,实际的QuickCheck机制要复杂得多。为了处理带有任意数量参数的函数,可以使用一组“递归” instance
,可以说每个“递归调用”都处理每个参数。这是一种相当棘手的技术,也用在printf
和其他“可变”函数中。如果您不熟悉类型类,建议您不要从这种复杂的技巧开始学习它们。
,
聚会晚了一点,但是this is the instance在当前实现中正在寻找。
instance (Arbitrary a,Show a,Testable prop) => Testable (a -> prop) where
property f =
propertyForAllShrinkShow arbitrary shrink (return . show) f
propertyForAllShrinkShow gen shr shw f =
-- gen :: Gen b,shr :: b -> [b],f :: b -> a -> prop
-- Idea: Generate and shrink (b,a) as a pair
propertyForAllShrinkShow
(liftM2 (,) gen arbitrary)
(liftShrink2 shr shrink)
(\(x,y) -> shw x ++ [show y])
(uncurry f)
正如@chi正确指出的,这里有递归发生。递归调用是propertyForAllShrinkShow
调用propertyForAllShrinkShow
,并且通过调用uncurry
,形式为a -> b -> c -> Bool
的属性将变成(a,b) -> c -> Bool
。由于(a,b)
是instance
的{{1}},因此Arbitrary (a,b)
是有效的任意值,Testable
的相同实例将再次运行,其中prop
是c -> Bool
。然后,将再次与((a,b),c
运行,然后prop
将只是Bool
。此时,将使用默认的propertyForAllShrinkShow
启动Bool
instance of Testable
,它创建了f x
的实际应用程序。因此,另一种说法是,快速检查会同时生成一个任意元组的所有值,并使用instance
的{{1}}中的递归构造元组。
本文链接:https://www.f2er.com/3144896.html