2011-09-17 2 views
1

내 haskell 코드를 원하는대로 모듈화 할 수 없습니다. 나는 아마도 객체 지향 패러다임에 갇혀 있고 기능적으로 생각하는 데 어려움을 겪고 있을지 모르지만 나는 완전히 곤두박질 친다.함수 정의를 여러 파일로 분할하여 모듈성이 향상됩니다.

conjunction :: TruthType -> TruthType -> TruthType 
conjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x*y) 
conjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
conjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

disjunction :: TruthType -> TruthType -> TruthType 
disjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x + (1-x)*y) 
disjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

이 컴파일 및 실행 :

data TruthType = TT_Boolean String 
       | TT_Percent Double 

conjunction :: TruthType -> TruthType -> TruthType 
disjunction :: TruthType -> TruthType -> TruthType 

일반적으로, 당신은 다음과 같이 서로 옆에 이러한 기능을 구현하는 것이 :

나는 데이터와 그것을 작동 두 가지 기능을 가지고 정확히 내가 기대했던 것처럼. 문제는 약 20 개의 서로 다른 TruthTypes를 구현할 계획이며, 각각에 대해 더 많은 기능을 제공한다는 것입니다. 이 섹션 모두 같은 파일에있는 경우

-- TT_Percent 
conjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x*y) 
disjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x + (1-x)*y) 

-- TT_Boolean 
conjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
conjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

disjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

, 내가 컴파일 오류가 나는 함께 재정의하고 있어요 주장 얻을 : 그래서 그들이에 작용하는 TruthType 생성자를 기반으로 그룹 내 기능을 더 의미 분리 함수. 나는 오래된 정의를 지우고 싶지 않다. 두 정의가 모두 유효하기를 바란다. 이 재 정의를 허용하기 위해 사용할 수있는 컴파일러 플래그가 있습니까?

궁극적으로 제 목표는 각기 다른 진실 유형을 자체 파일에 정의하는 것입니다. 그렇게하면 어떤 함수를 사용해야할지 모르기 때문에 모호한 오류가 발생합니다. GHC가 실제로 TruthType에 대해 정의 된 하나만 정의되었으므로 GHC에서 모든 것을 시도 할 수있는 방법이 있습니까?

추신. 이것은 타입 클래스를위한 훌륭한 유스 케이스처럼 보일지도 모르지만 실제로는 그렇지 않습니다.

class (Show a, Eq a) => TruthClass a where 
    conjunction :: a -> a -> a 
    disjunction :: a -> a -> a 

instance TruthClass Bool where 

    conjunction True True = True 
    conjunction True False = False 
    conjunction False True = False 
    conjunction False False = False 

    disjunction True True = True 
    disjunction True False = True 
    disjunction False True = True 
    disjunction False False = False 

instance TruthClass Double where 
    conjunction x y = x*y 
    disjunction x y = x + (1-x)*y 

classReturn :: (TruthClass a) => String -> a -- This fails to compile because it would allow the failure function below, which violates conjunction's type 
classReturn "True" = True 
classReturn "False" = False 
classReturn "1" = 1 
classReturn "0" = 0 

failure = conjunction (classReturn "True") (classReturn "1") 

편집 :

좋아, 내가 지금 잘 설명 할 수 나는 "인스턴스"TruthType의,이 예에서 "classReturn"기능 같은 것을 반환하는 함수를 쓸 수 있어야 왜 타입 클래스를 사용할 수 없으며, 왜 제안 된 솔루션이 나를 위해 작동하지 않는지. (아래 augustss의 솔루션을 기반으로) 다음에 봐 :

*Main> conjunction True True -- works because type is inferred 
True 

*Main> classReturn "True" :: Bool -- works because type is explicitly stated 
True 

*Main> classReturn "True" -- does not work, but this is what I need 

<interactive>:1:0: 
    Ambiguous type variable `a' in the constraint: 
     `TruthClass a' 
     arising from a use of `classReturn' at <interactive>:1:0-17 
    Probable fix: add a type signature that fixes these type variable(s) 

를 내 프로그램에서, 나는 그것이 유형을 지정할 수 없습니다. parsec을 사용하여 입력 파일을 구문 분석하고 있습니다. "#bool"행에 도달하면 이후에 생성되는 모든 변수는 TT_Boolean 유형이어야합니다. "#percent"에 도달하면 모든 후속 변수는 TT_Percent 유형이어야합니다. 따라서 함수를 호출 할 때 형식이 무엇인지 하드 코딩 할 수는 없으며 형식 클래스를 사용하는 경우 하드 코드해야합니다. 데이터를 사용하는 솔루션은이 문제를 해결하지만 데이터로 인해 모듈성이 부족합니다.

+0

'classReturn'도 오버로드하지 않는 이유는 무엇입니까? 그냥 수업의 구성원이되도록하십시오. – augustss

+0

BTW, 나는'x + y - x * y'가 대칭성을 더 잘 강조한다고 생각하지만 어쩌면 버전에 대한 숫자 적 이유가있을 수 있습니까? – augustss

+0

귀하의 의견에 뭔가 빠졌습니다. – augustss

답변

3
class (Read a, Show a, Eq a) => TruthClass a where 
    conjunction :: a -> a -> a 
    disjunction :: a -> a -> a 
    classReturn :: String -> a 
    classReturn = read 

instance TruthClass Bool where 
    conjunction = (&&) 
    disjunction = (||) 

instance TruthClass Double where 
    conjunction x y = x*y 
    disjunction x y = x + (1-x)*y 
+0

어떤 이유로 나는 함수가 클래스에있을 때 클래스를 매개 변수로 사용해야한다고 생각했습니다. 나는 약간의 주위를 놀아야 할 것이다. 그러나 나는 이것이 작동 할 것이라고 생각한다. 감사. –

+2

클래스 매개 변수를 반환 할 수 있다는 것은 OO 클래스보다 하스켈 유형 클래스의 장점 중 하나입니다. – augustss

+2

@Mike Izbicki : 클래스의 항목은 매개 변수조차 필요하지 않습니다. 클래스의 일부로 특정 값을 가질 수도 있습니다 - [Monoid' (http://hackage.haskell.org/packages/archive/base/4.4.0.0/doc/html/Data-Monoid)를보십시오. html)을 예로들 수 있습니다. 클래스 자체는 좀 더 추상적이라는 것을 기억하십시오. 유형 매개 변수는 인스턴스를 정의하는 데 사용되는 구체적인 유형을 나타냅니다. 당신이 할 수없는 유일한 방법은 전혀 언급하지 않는 것입니다. –

0

그러나 당신은 또한 당신이 conjunction 그 반대에 대한 방정식 사이 disjunction에 대한 방정식이 없어야 만한다는, 원래 디자인을 유지할 수 있습니다.

함수는 모든 방정식으로 구성되지만 소스 코드에서 연속적으로 발생해야합니다.

편집 : 예를 보여주는 방법 마이크 수행 할 수 있습니다 원하는 것을 :

당신은 많은 절은, 여러 사람에 당신에게 하나 개의 큰 기능을 분리 할 수있는 경우 :

conjunction PrincipleCase1 = conjunctionForCase1 ... 
conjunction PrincipleCase2 = conjunctionForCase2 ... 

다음을 다른 위치, 모듈 및 기타에 세부 사례를 전달하는 기능을 넣을 수 있습니다.

+0

보통 더 깨끗한 코드를 생성한다는 점을 제외하고는 연속적으로 발생해야하는 이유에 대해 생각할 수 없습니다. (예외는 물론 내 경우입니다.) 비 연속적인 정의를 허용하도록 강제하는 방법이있는 것처럼 보입니다. –

+0

마이크, 내 편집을 참조하십시오. – Ingo

관련 문제