2011-12-22 5 views
7

상황 :이 같은 오류도 경고 목록을 추적 모나드, 뭔가를 생산하기 위해 노력하고있어 :실존 종류와 모나드 변압기

data Dangerous a = forall e w. (Error e, Show e, Show w) => 
    Dangerous (ErrorT e (State [w]) a) 

Dangerous a(Either e a, [w])e을 초래하는 작업입니다 표시 가능한 오류이며 w이 표시됩니다.

문제는 내가 모든 것을 잘 실존 유형을 이해하지 못하는 대부분 때문에, 실제로 일을 실행할 수없는 것입니다. 관찰 :

runDangerous :: forall a e w. (Error e, Show e, Show w) => 
    Dangerous a -> (Either e a, [w]) 
runDangerous (Dangerous f) = runState (runErrorT f) [] 

이 컴파일되지 않습니다, 때문에 : 내가 잃었어요

Could not deduce (w1 ~ w) 
from the context (Error e, Show e, Show w) 
... 
`w1' is a rigidtype variable bound by 
    a pattern with constructor 
    Dangerous :: forall a e w. 
       (Error e, Show e, Show w) => 
       ErrorT e (State [w]) a -> Dangerous a 
... 
`w' is a rigid type variable bound by 
    the type signature for 
    runDangerous :: (Error e, Show e, Show w) => 
        Dangerous a -> (Either e a, [w]) 

. w1은 뭐니? 왜 그것이 ~ w이라고 추론 할 수 없습니까?

답변

12

실존은 여기에 원하는 아마 아니다; 거기 Dangerous ae 또는 w에 바인딩의 실제 유형을 "관찰"할 수있는 방법은 없다, 그래서 당신은 완전히 ErrorShow 제공 한 작업으로 제한하고 있습니다. 즉

, 당신이 알고있는 유일한 것은 약 w 당신이 String으로 바꿀 수 있으므로뿐만 아니라 당신이에 대한 e 알고 String (일을 단순화 무시 우선 순위), 유일한 일이 될 수 있다는 것입니다 당신이하는 String으로 바꿀 수 있습니다 당신이 그것으로 String들 설정할 수 있습니다, 당신이 그것을 (noMsg)의 고유 값을 가지고있다. 이러한 유형이 다른 유형과 동일하다는 것을 주장하거나 확인할 방법이 없으므로 Dangerous에 입력하면 해당 유형이 가질 수있는 특수 구조를 복구 할 수 없습니다. 오류 메시지가 무슨 말을

은 그 본질적으로, 당신은 에 대한 (Either e a, [w])Dangerous을 설정할 수 있습니다 runDangerous 주장에 대한 당신의 유형 어떤e 및 관련 인스턴스가 w. 이것은 분명히 사실이 아니다 : 당신은 단지 ew하나 선택에 대한 유형으로 Dangerous을 설정할 수 있습니다 :이 함께 만든 하나. w1Dangerous 유형이 유형의 변수 w으로 정의해서, 그리고 GHC 이름 충돌을 피하기 위해 그들 중 하나의 이름을 변경 있도록, 그래서 runDangerous입니다.

당신이 runDangerous을 줄 필요가 유형은 다음과 같습니다

runDangerous 
    :: (forall e w. (Error e, Show e, Show w) => (Either e a, [w]) -> r) 
    -> Dangerous a -> r 

하는, 너무 오래 그들은이 같은 어떤e의 선택과 w에 대한 유형 (Either e a, [w])의 값을 허용하는 기능을 제공 주어진 인스턴스 및 Dangerous a은 해당 함수의 결과를 생성합니다. 머리를 감싸는 일은 아주 어렵습니다!

구현은 간단

버전에 사소한 변화
runDangerous f (Dangerous m) = f $ runState (runErrorT m) [] 

있다. 이것이 당신을 위해 작동한다면, 위대한; 하지만 실존 적으로 당신이하고자하는 일을 달성하는 올바른 방법이라고는 생각하지 않습니다.

runDangerous 유형을 표현하려면 {-# LANGUAGE RankNTypes #-}이 필요합니다.

data DangerousResult a = forall e w. (Error e, Show e, Show w) => 
    DangerousResult (Either e a, [w]) 

runDangerous :: Dangerous a -> DangerousResult a 
runDangerous (Dangerous m) = DangerousResult $ runState (runErrorT m) [] 

case과 결과를 추출,하지만 당신은 조심해야합니다, 또는 GHC 당신이 e 또는 w 탈출을하게했다고 불평을 시작합니다 - : 또는 당신은 당신의 결과 유형에 대한 또 다른 존재를 정의 할 수 있습니다 불충분하게 다형 함수를 다른 형태의 runDangerous에 전달하려고 시도하는 것과 같습니다. 즉 ew에 대해 더 많은 제약을 요구하는 유형은 runDangerous 유형을 보장합니다. 좋아

+0

더 나은 (또는 관용적 인) 방법이 있습니까? 나는 정말로 단지 약간의 경고와 함께 오는 오류 모나드를 원한다. – So8res

+0

왜 형식을 '위험한 전자'로 정의하지 않는가? 당신이 달성하려고 노력하고있는 것을 이해한다면 여기에 존재할 필요가 없습니다. – ehird

+0

나는 모두 자신의 오류와 경고를 던지며 최상위 레벨에서 처리되는 몇 개의 모듈을 가지고있다. 최상위 레벨 만 인쇄하면되지만, 옵션 모듈에서는'위험한 OptError OptWarning [Option]', 템플릿 파일에서는'위험한 TemplateError TemplateWarning Template'이 모두'show'n 일 때 말하기가 짜증납니다. 나는 많은 상용어를 제거하고 약간 무언가를 배우는 것을 시도하고있다, 확실하게 근본적이지 않다. – So8res

1

, 나는 내가 이후에 몸부림 있었는지 알아 냈 생각 : (. instance Monad Dangerousdata DangerousT 도움도)

data Failure = forall e. (Error e, Show e) => Failure e 

data Warning = forall w. (Show w) => Warning w 

class (Monad m) => Errorable m where 
    warn :: (Show w) => w -> m() 
    throw :: (Error e, Show e) => e -> m() 

instance Errorable Dangerous where 
    warn w = Dangerous (Right(), [Warning w]) 
    throw e = Dangerous (Left $ Failure e, []) 

이것은 다음과 같은 코드를 가질 수 있습니다 :

foo :: Dangerous Int 
foo = do 
    when (badThings) (warn $ BadThings with some context) 
    when (worseThings) (throw $ BarError with other context) 

data FooWarning = BadThings FilePath Int String 
instance Show FooWarning where 
... 

메인 모듈에서 Show Failure, Error FailureShow Warning의 맞춤 인스턴스를 정의 할 수 있습니다. 차 예를

내 의견으로는, 오류 및 경고를 처리 할 수있는 정말 멋진 방법입니다,
instance Show Warning where show (Warning s) = "WARNING: " ++ show s 
instance Show Failure where ... 

let (result, warnings) = runDangerous function 
in ... 

를 들어, 오류 메시지를 포맷하는 중앙 집중식 방법이있다. 나는 이런 식으로 작동하는 모듈을 가지고 있습니다. 이제는 그 모듈을 닦고 아마 hackage에 넣을 것입니다. 제안을 감사드립니다.

+1

죄송합니다. 그러나'data Warning = forall w. (Show w) => Warning w는'data Warning = Warning String'과 같습니다. 경고 값에'warn' 함수 호출로'show'를 호출 할 수도 있습니다. 실존 인은 * 당신이 원하는 것을 실제로 여기 있지 않습니다. – ehird

+1

멋진 시스템 기능을 적용 해 주셔서 고맙게 생각하지만, 여기에있는 유일한 효과는 기능을 추가하지 않고 코드를 더 복잡하게 만드는 것입니다. – ehird

+0

설명 할 내용이 더 있습니다. [1] (http://www.haskell.org/haskellwiki/FAQ#How_do_I_make_a_list_with_elements_of_different_types.3F), [2] (http://lukepalmer.wordpress.com/2010/01/ 24/haskell-antipattern-existential-typecass /) -'data Foo = forall a와 같은 것을 사용하지 않는 이유에 대한 좋은 설명을 보았습니다. (표시 a) => 푸, ',하지만, 불행히도 지금 링크를 찾을 수 없습니다. – ehird

관련 문제