2010-05-20 15 views
9

는 이름에 기능이 동일하지만 서로 다른 것을 다음과 같이 내가 정의 된 두 가지 유형의 수업을 말 PhantomMonad의 인스턴스가 자동으로 Monad의 인스턴스가되거나 각 클래스의 인스턴스가 명시 적으로 작성되어야합니까? 모든 통찰력이 가장 감사 할 것입니다, 감사합니다!링크는/하스켈 타입 클래스를 결합

+2

'preturn :: a -> p b'는 오타입니까? –

답변

13

좋은 대답 : 아니, 당신이하고 싶은 것은 실제로 실행 가능하지 않습니다. 원하는대로 인스턴스를 작성하여 프로세스에서 일부 GHC 확장이 필요할 수도 있지만 원하는 방식으로 작동하지는 않습니다.

무의미한 대답 : 아마도 무서운 유형 수준의 메타 프로그래밍을 사용하여 원하는 것을 달성 할 수 있지만 복잡해질 수 있습니다. 절대적으로이 어떤 이유로 작동하지 않으면이 기능은 권장되지 않습니다.

GHC는 의사 결정을 할 때 "인스턴스 헤드"만보고 클래스 제약 조건은 "컨텍스트"에 있기 때문에 공식 인스턴스는 실제로 다른 인스턴스에 의존 할 수 없습니다. 여기 "유형 클래스 동의어"와 같은 것을 만들려면 가능한 모든 유형에 대해 Monad의 인스턴스처럼 보이는 것을 작성해야합니다. 이는 분명히 의미가 없습니다. 자체적으로 문제가있는 Monad의 다른 인스턴스와 겹칠 것입니다.

무엇보다도 이러한 인스턴스는 인스턴스 확인을위한 종료 확인 요구 사항을 충족시키지 않으므로 UndecidableInstances 확장이 필요합니다. 즉, GHC 유형을 전송할 인스턴스를 작성하는 기능을 의미합니다 무한 루프로 검사기.

정말로 토끼의 구멍을 내려 가려면 Oleg Kiselyov's website을 조금 둘러보십시오. 그는 하스켈에서 유형 수준 메타 프로그래밍의 수호 성인의 일종입니다.

확실히 재미있는 일이지만, 코드를 작성하고 작동 시키려면 고통의 가치가 없을 것입니다.

편집 : 좋아요, 추후에 나는이 문제를 과장했습니다. PhantomMonad 같은 것이 일회용으로 잘 작동하며 Overlapping - 및 UndecidableInstances GHC 확장을 사용하면 원하는대로 수행해야합니다. 복잡한 문제는 질문에있는 것보다 훨씬 복잡한 작업을 원할 때 시작됩니다. 저에게 전화를 한 Norman Ramsey에게 진심으로 감사드립니다. 나는 더 잘 알았어야했습니다.

나는 아직도 실제로 좋은 이유없이 이런 종류의 일을하는 것이 좋습니다.하지만 소리가 나쁘지는 않습니다. Mea culpa.

+0

감사! 만약 내가 혼란스러워하는 사고로 명백한 것을 놓친 것이거나 이것이 정말로 엉망이 되었다면 나는 궁금했다. 말할 것도없이, 나는 "좋은"대답을 고수하고있다. – thegravian

+0

@ thegravian : 현명한 결정. 그것이 도움이된다면, 당신의 아이디어가 본질적으로 어리석은 것이 아니라, 하스켈의 타입 클래스 시스템이 주어진대로 작동하지 않습니다. 나는 그것을 작동하게하는 몇 가지 제안 된 확장이 있다고 생각하지만 지금까지 구현 된 확장은 없다. –

+0

@camcann : 그 해결책은 실제로 모두 * 무서운 것이 아닙니다. 그렇습니까?내 말은, "undecidable"이라는 단어는 조금 무서운 것이지만, 하스켈 타입 검사는 이미 기하 급수적으로 완료되었으므로, 내가 정말로하고 싶었던 것을하지 못하게 할 것입니다. –

6

그건 특이한 디자인입니다. PhantomMonad는 다른 클래스와 동형이기 때문에 제거 할 수 없습니까?

+0

당신 말이 맞아요, 그 디자인은 실행 가능하지 않습니다 (그리고 정말로 무의미합니다). 나는 이것을하지 않기로 마음 먹었지 만, 하스켈이 이런 일을 할 수있는 일종의 시설을 가지고 있다면 나는 여전히 궁금해하고 있었다. 이것은 아마도 정상적인 사람들이하지 않기 때문에 가능하지 않습니다. – thegravian

+3

비슷한 경우는'mtl'과'transformers'의'MonadTrans' 클래스가 될 것입니다. 그들은 정확히 동일합니다. 이 간단한 비전의 예제에서 클래스를 통합하는 방법에 대한 대답은 실제 코드에서도 대단히 유용 할 수있었습니다. – yairchu

+2

@yairchu : 공정하게 말하자면, "하나의 클래스 제거"솔루션이이 케이스에 대한 올바른 해결책입니다. 즉, 'mtl'을 제거하는 것입니다 ... –

1

이 정말하지 않습니다 의미하지만,

instance Monad m => PhantomMonad m where 
    pbind = (>>=) 
    preturn = return 

(어쩌면 비활성화 일부 컴파일러 경고를)보십시오.

+0

그는 그가 요구 한 것에서 다른 방향으로 나아갔습니다. 물론,'인스턴스 (PhantomMonad m) => Monad m where ... '는 더 많은 문제를 일으킬 것입니다. –

7

PhantomMonad의 인스턴스 인 무언가가 자동으로 Monad의 인스턴스가 될 수 있도록이 두 클래스를 서로 묶는 방법이 있습니까?

예,하지만 약간 놀라운 언어 확장 FlexibleInstancesUndecidableInstances 필요

instance (PhantomMonad m) => Monad m where 
    return = preturn 
    (>>=) = pbind 

FlexibleInstances 그렇게 나쁘지는 않지만 결정 불가능의 위험이 약간 더 놀라운입니다. 문제는 추론 규칙에서 아무것도 작아지고 있지 않기 때문에이 인스턴스 선언을 다른 유사한 인스턴스 (예 : 역방향)와 결합하면 형식 검사기가 쉽게 반복 될 수 있습니다.

나는 일반적으로 FlexibleInstances을 사용하는 것이 편안하지만, 나는 아주 좋은 이유없이 UndecidableInstances을 피하는 경향이있다. 여기서 나는 Don Stewart의 제안에 동의하는 것으로 시작하는 것이 더 편할 것이라고 Monad에 동의한다. 그러나 당신의 질문은 사고 실험의 성격 상 더 중요합니다. 답은 당신이 Oleg 수준의 희소성에 빠지지 않고 원하는 것을 할 수 있다는 것입니다.

+0

'Monad'의 모든 다른 인스턴스를 겹치기 때문에 이것은 작동하지 않습니다. . 그러나 물건을 부수기 위해 놀고 난 후에 놀고 난 후에, 그것은 GHC가 내가 단일의 매개 변수 타입 클래스로 생각했던 것보다 똑똑한 것처럼 보인다. 분명히 이와 같은 일반적인 인스턴스를 하나만 얻을 수 있지만 일회용으로 'OverlappingInstances'를 사용하면 문제가 없습니다. –

3

다른 해결책은 newtype을 사용하는 것입니다. 이것은 정확히 원하는 것은 아니지만 그러한 경우에 자주 사용됩니다.

이렇게하면 동일한 구조를 지정하는 여러 가지 방법을 연결할 수 있습니다. 예를 들어 ArrowApply (Control.Arrow에서 제공) 및 Monad은 동일합니다. Kleisli을 사용하여 모나드에서 ArrowApply을 만들고 ArrowMonad을 사용하여 모서리를 ArrowApply에서 만들 수 있습니다.

또한 단방향 래퍼가 가능합니다. WrapMonad (Control.Applicative)은 모나드에서 응용 프로그램을 만듭니다.

class PhantomMonad p where 
    pbind :: p a -> (a -> p b) -> p b 
    preturn :: a -> p a 

newtype WrapPhantom m a = WrapPhantom { unWrapPhantom :: m a } 

newtype WrapReal m a = WrapReal { unWrapReal :: m a } 

instance Monad m => PhantomMonad (WrapPhantom m) where 
    pbind (WrapPhantom x) f = WrapPhantom (x >>= (unWrapPhantom . f)) 
    preturn = WrapPhantom . return 

instance PhantomMonad m => Monad (WrapReal m) where 
    WrapReal x >>= f = WrapReal (x `pbind` (unWrapReal . f)) 
    return = WrapReal . preturn 
+1

'Control.Arrow'에서'WrapMonad'를 찾을 수 없지만'ArrowApply a => Monad (a) (인스턴스) '인스턴스를 찾을 수 없습니다. 'newtype Kleisli m a b = Kleisli (a -> m b)'; '인스턴스 Monad m => ArrowApply (Kleisli m)'; 이 인스턴스들을 왕복으로 돌아 다니면 시작한 것과 동일한 'ArrowApply'에서 돌아 가지 않습니다. 'pre a x y = a x y','post a x y = x -> a() y'. – yairchu

+0

와우, 나는 ArrowMonad를 의미했습니다. – sdcvvc