İşin püf noktası, tür sınıflarını kullanmaktır. Durumda printf, anahtar PrintfTypetü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 printfaşırı yüklenmiş bir dönüş türü vardır. Biz örneğini gerekiyor çok önemsiz durumda, biz hiçbir ekstra argümanlar var riç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 ra ise PrintfType, bir işlev türü x -> rde 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 PrintfArgdevreye girdiği yer burasıdır . Yani gerçek örnek
instance (PrintfArg x, PrintfType r) => PrintfType (x -> r)
ShowSı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, barargü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, Testablesınıfın temel durum için bir örneğe sahip olduğu ve sınıfta Boolbağı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)