۱۶ - ۹تست کردن نمونههای Functor با QuickCheck
با قوانینِ Functor
آشنا شدیم:
fmap id = id
fmap (p . q) = (fmap p) . (fmap g)
میشه اونها رو اینطوری به مشخصههای QuickCheck
تبدیل کنیم:
functorIdentity :: (Functor f, Eq (f a)) =>
f a
-> Bool
functorIdentity f =
fmap id f == f
functorCompose :: (Eq (f c), Functor f) =>
(a -> b)
-> (b -> c)
-> f a
-> Bool
functorCompose f g x =
(fmap g (fmap f x)) == (fmap (g . f) x)
با تأمین تایپهای معیّن، میشه این تستها رو انجام بدیم:
Prelude> :{
*Main| let f :: [Int] -> Bool
*Main| f x = functorIdentity x
*Main| :}
Prelude> quickCheck f
+++ OK, passed 100 tests.
Prelude> let c = functorCompose (+1) (*2)
Prelude> let li x = c (x :: [Int])
Prelude> quickCheck li
+++ OK, passed 100 tests.
خیلی عالی.
کاری کنیم QuickCheck
تابع هم ایجاد کنه
QuickCheck قابلیتِ ایجاد ِ توابع هم داره. تایپکلاسی به اسمِ CoArbitrary
داره که تایپِ آرگومان تابع رو پوشش میده؛ و در مقابل، تایپکلاس Arbitrary
(که بیربط هم نیست) برای تایپِ نتیجهی تابع استفاده میشه. اگه میخواین بیشتر بدونین، یه نگاه به ماژول ِ Function
از کتابخونه ِ QuickCheck
بندازین تا ببینید چطور از نوعدادهای که ساختِ تابع رو نشون میده، توابع ایجاد میشن.
{-# LANGUAGE ViewPatterns #-}
import Test.QuickCheck
import Test.QuickCheck.Function
functorCompose' :: (Eq (f c), Functor f) =>
f a
-> Fun a b
-> Fun b c
-> Bool
functorCompose' x (Fun _ f) (Fun _ g) =
(fmap (g . f) x) == (fmap g . fmap f $ x)
اینجا چندتا کار انجام دادیم. یکی اینکه باید یه ماژول ِ جدید از QuickCheck
وارد میکردیم. دیگه اینکه روی مقدارِ Fun
که از QuickCheck
خواستیم ایجاد کنه تطبیق الگو انجام دادیم. تایپ Fun
در حقیقت حاصلضرب ِ یه جور تابع با تایپی عجیب (مختصِ خودِ QuickCheck
) و یه تابعِ معمولیِ هسکله. ما فقط با بخشِ دومش کار داریم، یعنی اون تابعِ معمولی، پس با تطبیق الگو میکشیمش بیرون.
Prelude> type IntToInt = Fun Int Int
Prelude> :{
*Main| type IntFC =
*Main| [Int]
*Main| -> IntToInt
*Main| -> IntToInt
*Main| -> Bool
*Main| :}
Prelude> let fc' = functorCompose'
Prelude> quickCheck (fc' :: IntFC)
+++ OK, passed 100 tests.
حواستون باشه که اون مقادیرِ Fun
رو نمیشه چاپ کرد، پس verboseCheck
کار نمیکنه.