트릭은 유형 클래스를 사용하는 것입니다. 의 경우 printf
키는 PrintfType
유형 클래스입니다. 메서드를 노출하지 않지만 중요한 부분은 어쨌든 형식에 있습니다.
class PrintfType r
printf :: PrintfType r => String -> r
따라서 printf
오버로드 된 반환 유형이 있습니다. 사소한 경우에는 추가 인수가 없기 때문에으로 인스턴스화 할 수 있어야 r
합니다 IO ()
. 이를 위해 인스턴스가 있습니다.
instance PrintfType (IO ())
다음으로 가변 개수의 인수를 지원하려면 인스턴스 수준에서 재귀를 사용해야합니다. 경우 있도록 특히 우리는 인스턴스를 필요 r
입니다 PrintfType
, 함수 타입 x -> r
도있다 PrintfType
.
-- instance PrintfType r => PrintfType (x -> r)
물론 우리는 실제로 형식을 지정할 수있는 인수 만 지원하려고합니다. 이것이 두 번째 유형 클래스 PrintfArg
가 들어오는 곳입니다. 따라서 실제 인스턴스는
instance (PrintfArg x, PrintfType r) => PrintfType (x -> r)
다음은 Show
클래스 에서 여러 인수를 가져 와서 인쇄 하는 단순화 된 버전입니다 .
{-# 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)
여기서는 bar
더 이상 인수가 없을 때까지 재귀 적으로 생성되는 IO 액션을 취합니다.이 시점에서 단순히 실행합니다.
*Main> foo 3 :: IO ()
3
*Main> foo 3 "hello" :: IO ()
3
"hello"
*Main> foo 3 "hello" True :: IO ()
3
"hello"
True
QuickCheck는 또한 동일한 기술을 사용합니다. 여기서 Testable
클래스에는 기본 케이스에 대한 인스턴스 Bool
가 있고 Arbitrary
클래스 에서 인수를받는 함수에 대한 재귀 인스턴스 가 있습니다 .
class Testable a
instance Testable Bool
instance (Arbitrary x, Testable r) => Testable (x -> r)