2012-10-03 5 views
7

오늘은 형식 클래스를 사용하여 모든 유형의 조합을 입력으로 사용하여 같은 유형의 다른 조건자를 반환하지만 일부 기본 연산을 적용한 임의의 조건의 조건 자의 함수를 유도 적으로 작성했습니다. 예를 들어하스켈에서 어떻게 m-ary 술어와 n-ary 술어를 취해 (m + n) -ary 술어를 만들 수 있습니까?

conjunction (>2) even 

두와

보다 큰 짝수 true로 평가되는 술어를 반환
conjunction (>=) (<=) 

는 =

과 좋은있어 그 부분의 작업을 반환하지만 것 두 개의 술어의 결합을 각 결합 된 술어의 각 입력에 대해 하나의 입력을 취하는 술어로 정의하고 싶다면 어떻게해야할까요? 예 :

:t conjunction (>) not 

은 Ord a => a -> a -> Bool -> Bool을 반환합니다. 이 작업을 수행 할 수 있습니까? 그렇다면 어떻게?

답변

6

이 솔루션에는 TypeFamilies이 필요합니다.

{-# LANGUAGE TypeFamilies #-} 

아이디어는 n 차 술어에 대한 클래스 Pred을 정의하는 것입니다 : 문제는 모든 술어에 다시 셔플 인수에 관한

class Pred a where 
    type Arg a k :: * 
    split :: a -> (Bool -> r) -> Arg a r 

를, 그래서 이것은 클래스가 수행하는 것을 목표로 무엇인가 . 연관된 유형 Argk으로 최종 Bool를 교체하여 n 차 술어의 인수에 대한 액세스 권한을 부여하도록되어, 그래서 우리는 유형 다음

X = arg1 -> arg2 -> ... -> argn -> Bool 

Arg X k = arg1 -> arg2 -> ... -> argn -> k 

이 경우에 허용 우리는 conjunction의 올바른 결과 유형을 작성하여 두 술어의 모든 인수를 수집해야합니다.

함수 splita 유형의 술어와 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) ka ->로 시작해야합니다, 우리는 r에 재귀 Arg를 호출하여 계속합니다. split의 경우 이제 xa 인 3 개의 인수를 사용할 수 있습니다. xf에 공급 한 다음 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 같은) 다른 함수를 작성하는 사소한 것을, 너무.

4
{-# LANGUAGE TypeFamilies #-} 

class RightConjunct b where 
    rconj :: Bool -> b -> b 

instance RightConjunct Bool where 
    rconj = (&&) 

instance RightConjunct b => RightConjunct (c -> b) where 
    rconj b f = \x -> b `rconj` f x 

class LeftConjunct a where 
    type Ret a b 
    lconj :: RightConjunct b => a -> b -> Ret a b 

instance LeftConjunct Bool where 
    type Ret Bool b = b 
    lconj = rconj 

instance LeftConjunct a => LeftConjunct (c -> a) where 
    type Ret (c -> a) b = c -> Ret a b 
    lconj f y = \x -> f x `lconj` y 

conjunction :: (LeftConjunct a, RightConjunct b) => a -> b -> Ret a b 
conjunction = lconj 

희망 사항은 자명하지만, 그렇지 않은 경우 언제든지 질문 할 수 있습니다.

물론 두 클래스를 하나의 클래스로 병합 할 수도 있지만 두 클래스가 아이디어를 더 분명하게 만듭니다.

관련 문제