이 솔루션에는 TypeFamilies
이 필요합니다.
{-# LANGUAGE TypeFamilies #-}
아이디어는 n 차 술어에 대한 클래스 Pred
을 정의하는 것입니다 : 문제는 모든 술어에 다시 셔플 인수에 관한
class Pred a where
type Arg a k :: *
split :: a -> (Bool -> r) -> Arg a r
를, 그래서 이것은 클래스가 수행하는 것을 목표로 무엇인가 . 연관된 유형 Arg
은 k
으로 최종 Bool
를 교체하여 n 차 술어의 인수에 대한 액세스 권한을 부여하도록되어, 그래서 우리는 유형 다음
X = arg1 -> arg2 -> ... -> argn -> Bool
Arg X k = arg1 -> arg2 -> ... -> argn -> k
이 경우에 허용 우리는 conjunction
의 올바른 결과 유형을 작성하여 두 술어의 모든 인수를 수집해야합니다.
함수 split
은 a
유형의 술어와 Bool -> r
유형의 연속형을 취하며 Arg a r
유형을 생성합니다. split
의 아이디어는 우리가 Bool
으로 무엇을해야 하는지를 알면 최종적으로 술어에서 얻습니다. 그러면 다른 것들()을 사이에 넣을 수 있습니다. 그래서 Arg Bool k
단순히 k
반환,
instance Pred Bool where
type Arg Bool k = k
split b k = k b
Bool
는 인수가 없습니다 :
놀라 울 정도로, 우리는 대상이 이미 술어되는 두 개의 인스턴스, 기능
Bool
하나 하나가 필요합니다하지 않습니다.또한
split
의 경우
Bool
이 이미 있으므로 즉시 적용 할 수 있습니다. 우리는 유형
a -> r
의 술어가있는 경우
instance Pred r => Pred (a -> r) where
type Arg (a -> r) k = a -> Arg r k
split f k x = split (f x) k
, 다음 Arg (a -> r) k
는 a ->
로 시작해야합니다, 우리는 r
에 재귀 Arg
를 호출하여 계속합니다. split
의 경우 이제 x
이 a
인 3 개의 인수를 사용할 수 있습니다. x
을 f
에 공급 한 다음 split
으로 전화를 걸 수 있습니다. 우리가 Pred
클래스를 정의하면
, conjunction
을 정의하기 쉽습니다 :
는
conjunction :: (Pred a, Pred b) => a -> b -> Arg a (Arg b Bool)
conjunction x y = split x (\ xb -> split y (\ yb -> xb && yb))
기능은 두 가지 조건을 받아 형 Arg a (Arg b Bool)
의 무언가를 반환합니다. 이 예를 살펴 보겠습니다.
> :t conjunction (>) not
conjunction (>) not
:: Ord a => Arg (a -> a -> Bool) (Arg (Bool -> Bool) Bool)
GHCi는이 유형을 확장하지 않지만 가능합니다. 유형은
Ord a => a -> a -> Bool -> Bool
과 동일합니다. 이는 정확히 우리가 원하는 것입니다. 우리는 너무 많은 예제를 테스트 할 수 있습니다
> conjunction (>) not 4 2 False
True
> conjunction (>) not 4 2 True
False
> conjunction (>) not 2 2 False
False
주를 Pred
클래스를 사용하여, (disjunction
같은) 다른 함수를 작성하는 사소한 것을, 너무.