İşin püf noktası, tür sınıflarını kullanmaktır. Durumda printf
, anahtar PrintfType
tür sınıfıdır. Herhangi bir yöntemi açığa çıkarmaz, ancak önemli olan kısım zaten türlerdedir.
class PrintfType r
printf :: PrintfType r => String -> r
Yani printf
aşırı yüklenmiş bir dönüş türü vardır. Biz örneğini gerekiyor çok önemsiz durumda, biz hiçbir ekstra argümanlar var r
için IO ()
. Bunun için örneğimiz var
instance PrintfType (IO ())
Sonra, değişken sayıda argümanı desteklemek için, örnek düzeyinde özyineleme kullanmamız gerekir. Özellikle bir örneğe ihtiyacımız var, böylece r
a ise PrintfType
, bir işlev türü x -> r
de a PrintfType
.
-- instance PrintfType r => PrintfType (x -> r)
Tabii ki, sadece gerçekten biçimlendirilebilen argümanları desteklemek istiyoruz. İkinci tür sınıfın PrintfArg
devreye girdiği yer burasıdır . Yani gerçek örnek
instance (PrintfArg x, PrintfType r) => PrintfType (x -> r)
Show
Sınıftaki herhangi bir sayıda argümanı alan ve yalnızca bunları yazdıran basitleştirilmiş bir sürüm :
{-# LANGUAGE FlexibleInstances #-}
foo :: FooType a => a
foo = bar (return ())
class FooType a where
bar :: IO () -> a
instance FooType (IO ()) where
bar = id
instance (Show x, FooType r) => FooType (x -> r) where
bar s x = bar (s >> print x)
Burada, bar
argüman kalmayıncaya kadar özyinelemeli olarak oluşturulan bir IO eylemi alır, bu noktada onu basitçe çalıştırırız.
*Main> foo 3 :: IO ()
3
*Main> foo 3 "hello" :: IO ()
3
"hello"
*Main> foo 3 "hello" True :: IO ()
3
"hello"
True
QuickCheck ayrıca, Testable
sınıfın temel durum için bir örneğe sahip olduğu ve sınıfta Bool
bağımsız değişkenler alan işlevler için özyinelemeli bir örneğe sahip olduğu aynı tekniği kullanır Arbitrary
.
class Testable a
instance Testable Bool
instance (Arbitrary x, Testable r) => Testable (x -> r)