2011-04-27 4 views
1

다른 재귀 유형 클래스처럼 작동하지만 "상위"클래스만큼 인스턴스가없는 재귀 유형 클래스는 어떻게 작성합니까? 여기 재귀 유형 클래스의 서브 세트에서 유형 클래스 (또는 재귀 유형에서 유형)

은 예입니다

data Atom = Atom 
data (Formula a) => Negation a = Negation a 

class Formula a where 
instance Formula Atom where 
instance (Formula a) => Formula (Negation a) where 

class SubFormula a where 
instance SubFormula Atom where 

그 코드가 잘 컴파일하지만, 서브 타입 클래스

formulaToSubFormula :: (Formula a, SubFormula b) => a -> b 
formulaToSubFormula _ = Atom 

중 하나에 슈퍼 타입 클래스의 인스턴스를 변환하는 기능을 추가 할 때 나는 오류를 얻을

test.hs:12:25: 
    Could not deduce (b ~ Atom) 
    from the context (Formula a, SubFormula b) 
     bound by the type signature for 
       formulaToSubFormula :: (Formula a, SubFormula b) => a -> b 
     at test.hs:12:1-28 
     `b' is a rigid type variable bound by 
      the type signature for 
      formulaToSubFormula :: (Formula a, SubFormula b) => a -> b 
      at test.hs:12:1 
    In the expression: Atom 
    In an equation for `formulaToSubFormula': 
     formulaToSubFormula _ = Atom 

원래의 의도는 일반적인 유형으로 이것을하는 것이었지만 유형 클래스를 사용하면 문제가 더 친숙하고 일반적으로보다 유연 해 보입니다. 예를 들어

: I 입력 유형에 대한 조작은 일부를 반환하는 형식 수준에 확인하려면 :

data Formula = Atom | Negation Formula | Conjunction Formula Formula 
data SubFormula = Atom | Negation SubFormula 

편집

내가 달성하기 위해 무엇을하려고 명확히하려면 그 결과로 그 유형의.

확장 예 (명제 논리 아니오 유효한 하스켈 구문)

data Formula = Atom 
      | Negation Formula 
      | Disjunction Formula Formula 
      | Implication Formula Formula 
data SimpleFormula = Atom 
        | Negation SimpleFormula 
        | Disjunction SimpleFormula SimpleFormula 

-- removeImplication is not implemented correctly but shows what I mean 
removeImplication :: Formula -> SimpleFormula 
removeImplication (Implication a b) = (Negation a) `Disjunction` b 
removeImplication a = a 

나중 시점에서 I는 논리 곱 표준형의 수식 (NO 유효 하스켈 구문)

data CNF = CNFElem 
     | Conjunction CNF CNF 
data CNFElem = Atom 
      | Negation Atom 
      | Disjunction CNFElem CNFElem 

를 가질 수있다 따라서이 계층 구조를 나타내는 도구가 필요합니다.

+0

'removeImplication' 계획에서 가장 외적인'Implication' 생성자 만 제거한다는 것에주의하십시오. 'removeImplication (Negation (Implication x y))'는'(Negation (Implication x y))'가됩니다. 간단한 솔루션은'Formula'와'NormalForm'과'toNormalForm' 두 가지 타입을 가지는데, 복잡한 공식을 거치고 시적을 적절한 NormalForm으로 재귀 적으로 변환합니다. 나는 우리가 아직도 당신이 어디로 가고 있는지에 대해 더 알 필요가 있다고 생각합니다. – applicative

+0

@applicative :'removeImplication' 함수는 내가 의미하는 바를 보여주기위한 것입니다. 사실,'NormalForm'은'Formula'의 부분 집합입니다. 그래서이 두 유형을 공유 할 수있는 방법을 찾고 있습니다. –

+0

당신의 더 넓은 목적은 여전히 ​​불투명합니다. 예를 들어, '스티븐 테 틀리 (Stephen tetley)'가 언급 한 경로 중 하나를 택할 가치가 있는지 여부. 데이터 유형 정의, 특히 재귀 적 데이터 유형 정의는 축소 형 언어와 같습니다. 당신은 몇 가지 작은 언어와 그들의 관계를 고려하고 있습니다. 왜 이러한 관계가 유형 * 사이의 함수로 표현되어서는 안 되는가? 'L1'과'L2'가 타입이라면,'L1-> L2'와'L2-> L1' 타입이 그것들 사이의 관계 유형입니다. – applicative

답변

0

I 데이터 유형의 중첩 제한 조건을 나타내는 것으로 유일한 방법은 이런 타입 클래스를 통해 룰 시스템의 일종이다

data Formula t val = Formula val deriving Show 

-- formulae of type t allow negation of values of type a 

class Negatable t a 
instance Negatable Fancy a 
instance Negatable Normal a 
instance Negatable NNF Atom 
instance Negatable CNF Atom 
instance Negatable DNF Atom 

class Conjunctable t a 
instance Conjunctable Fancy a 
instance Conjunctable Normal a 
instance Conjunctable NNF a 
instance Conjunctable CNF a 
instance Conjunctable DNF Atom 
instance Conjunctable DNF (Negation a) 
instance Conjunctable DNF (Conjunction a b) 

--- 

negate :: (Negatable t a) => Formula t a -> Formula t (Negation a) 
negate (Formula x) = Formula $ Negation x 

conjunct :: (Conjunctable t a, Conjunctable t b) 
     => Formula t a -> Formula t b -> Formula t (Conjunction a b) 
conjunct (Formula x) (Formula y) = Formula $ Conjunction x y 

언급 한 논문, 특히 Data types a la cart은 정말 도움이되었습니다.

2

하스켈 typeclasses는 다음과 같이 작동하지 않습니다 서브 타입 클래스 중 하나에 슈퍼 타입 클래스의 인스턴스로 변환합니다.

강제 변환 또는 하위 유형 지정을 제공하지 않습니다. Atom을 리턴하는 함수는 Atom 값을 빌드하는 명시 적 생성자를 리턴하므로 리턴 유형이 Atom 일 수 있습니다.

이 같은 모델링 표현 언어의

, 대수 데이터 유형을 (또는 때때로, 일반화 대수 데이터 유형)을 압도적으로 선호하는 옵션은 다음과 같습니다 매개 변수 유형 임의적으로 표현 할 수

data Proposition 
    = LITERAL Bool 
    | ALL (Set Proposition) 
    | ANY (Set Proposition) 
    | NOT Proposition 

, 또는 GADT에 따라 다릅니다.

1

코드의 문제는 formulaToSubFormula _ = Atom에서 출력이 Atom 생성자로 생성된다, 그래서 유형 서명은 SubFormula 인스턴스 모든 종류의 수를 선언하는 반면 그것은 항상 타입 Atom이다.하나의 옵션은 SubFormula 클래스에 기능을 추가하는 것입니다

formulaToSubFormula2 :: Formula a => a -> Atom 

이 또한주의 : 당신은 단지 하위 유형의 인스턴스를 사용할 경우에 물론

class SubFormula a where 
    atom :: a 

instance SubFormula Atom where 
    atom = Atom 

formulaToSubFormula :: (Formula a, SubFormula b) => a -> b 
formulaToSubFormula _ = atom 

, 당신은 완전히 클래스를 생략 할 수 있습니다 그

data (Formula a) => Negation a = Negation a 

아마 당신이 원하는 것을하지 않습니다. 의도는 아마도 Formula a 유형 만 무효화 될 수 있으며 항상 Formula 컨텍스트를 사용할 수 있지만 Negation a을 사용할 때마다이 컨텍스트를 사용하지 않아도 Formula a 컨텍스트를 제공해야합니다. 당신은 GADT syntax으로이를 작성하여 원하는 결과를 얻을 수 있습니다 :

여기에 갈 수있는 많은 것들이 있습니다
data Negation a where 
    Negation :: Formula a => a -> Negation a 
1

, 내가 어떤 유형 클래스의 도입을 포함한다는 의심한다. (여기에있는 멋진 일은 GADT입니다.) 다음은 매우 단순합니다. 그것은 단지 당신이 더 명확하게 원하는 것을 말할 수 있도록하기위한 것입니다 ....

다음은 원래 가지고 있던 것과 같은 다형성 유형입니다. 다형성이므로 아무 것도 사용하여 원자 수식을 나타낼 수 있습니다. 여기

data Atoms = P | Q | R | S | T | U | V deriving (Show, Eq, Ord) 

이 일부입니다 : 여기
subformulas (Atom a) = [Atom a] 
subformulas (Negation p) = Negation p : subformulas p 
subformulas (Conjunction p q) = Conjunction p q : (subformulas p ++ subformulas q) 

당신이 매우 많은 원자 명제를 고민하지 않는 경우에 사용하는 유형입니다 : 여기
data Formula a = Atom a 
       | Negation (Formula a)  
       | Conjunction (Formula a) (Formula a) deriving (Show, Eq, Ord) 

모든 subformulas를 추출하는 기능입니다 무작위 도우미 :

k p q = Conjunction p q 
n p = Negation p 
p = Atom P 
q = Atom Q 
r = Atom R 
s = Atom S 

x --> y = n $ k x (n y) -- note lame syntax highlighting 

-- Main> ((p --> q) --> q) 
-- Negation (Conjunction (Negation (Conjunction (Atom P) (Negation (Atom Q)))) (Negation (Atom Q))) 
-- Main> subformulas ((p --> q) --> q) 
-- [Negation (Conjunction (Negation (Conjunction (Atom P) (Negation (Atom Q)))) (Negation (Atom Q))), 
--  Conjunction (Negation (Conjunction (Atom P) (Negation (Atom Q)))) (Negation (Atom Q)), 
--  Negation (Conjunction (Atom P) (Negation (Atom Q))), 
--  Conjunction (Atom P) (Negation (Atom Q)),Atom P, 
--  Negation (Atom Q),Atom Q,Negation (Atom Q),Atom Q] 

부울 원자를 만들자! :

t = Atom True 
f = Atom False 

-- Main> t --> f 
-- Main> subformulas (t --> f) 
-- [Negation (Conjunction (Atom True) (Negation (Atom False))), 
--   Conjunction (Atom True) (Negation (Atom False)),  
--   Atom True,Negation (Atom False),Atom False] 

왜 간단한 평가 기능이 아니겠습니까?

eval :: Formula Bool -> Bool 
eval (Atom p) = p 
eval (Negation p) = not (eval p) 
eval (Conjunction p q) = eval p && eval q 

몇 임의 결과 :

-- Main> eval (t --> f) 
-- False 
-- Main> map eval $ subformulas (t --> f) 
-- [False,True,True,True,False] 

나중에 추가는 : Formula 당신이 유도 절과 언어은 Functor를 추가 할 경우 GHC에서 유추 할 수있는 명백한 예와 Functor이므로주의 pragma {-#LANGUAGE DeriveFunctor#-}. 그럼 당신은 진실 값의 할당으로 어떤 기능 f :: a -> Bool을 사용할 수 있습니다 : 그것은 아주 오래이고 내가 서식을 원했기 때문에 나는이 대답했습니다

-- *Main> let f p = p == P || p == R 
-- *Main> fmap f (p --> q) 
-- Negation (Conjunction (Atom True) (Negation (Atom False))) 
-- *Main> eval it 
-- False 
-- *Main> fmap f ((p --> q) --> r) 
-- Negation (Conjunction (Negation (Conjunction (Atom True) (Negation (Atom False)))) (Negation (Atom True))) 
-- *Main> eval it 
-- True 
2

. 실제로, 나는 해결책이라고 생각보다 의견이 많기 때문에 의견이라고 생각합니다.

당신이 특정일반에서 사용자의 요구를 같이 분석하고 있지만 확장/모듈 형 구문을하고자하는 것 같습니다

- 확장 구문에 대한 대부분의 서면 다른보기를 받아 수 있도록 여분의 경우 추가 고려합니다 "작은" 더 큰 구문.

하스켈에서 확장 가능한 구문을 구현하는 방법이 있습니다. "마지막으로 Tagless"스타일 [1] 또는 Sheard와 Pasalic의 두 레벨 유형 [2].

실제로 모듈 식 구문을 얻기위한 "프로토콜"코드는 복잡하고 반복적이어서 일반 Haskell 데이터 유형, 특히 패턴 일치의 멋진 기능을 잃어 버립니다. 당신은 또한 lot의 명료성을 잃습니다. 이 마지막 비트는 매우 중요합니다. 모듈 식 구문은 사용의 가치가 거의없는 선명도와 같은 "세금"입니다. 일반적으로 현재 문제와 정확히 일치하는 데이터 유형을 사용하는 것이 좋습니다. 나중에 소스 코드를 편집해야하는 경우이를 확장해야합니다.

[1] http://www.cs.rutgers.edu/~ccshan/tagless/jfp.pdf

[2] http://homepage.mac.com/pasalic/p2/papers/JfpPearl.pdf

+0

2 레벨 방식의 접근 방식이 유망 해 보이며 더 자세히 살펴 보겠습니다. 다른 논문은 내 목적에 너무 복잡해 보입니다. –