2014-02-08 3 views
2

나는 언어의 흥미있는 가장자리 경우에 여기에서 붙이게되는 것처럼 보인다. 내가 할 노력하고있어 설명, 그래서 내가 대신 코드를 작성할 수 있도록 까다로운 :강제로 유령의 팬텀 타입

data Foobar x = 
    Foo1 {field1 :: x, field2 :: String} | 
    Foo2 {field1 :: x, field3 :: Int} | 
    Foo3 {    field4 :: Bool} | 
    Foo4 {    field2 :: String, field4 :: Bool} 

당신이 볼 수 있듯이, 일부 생성자 x에 의존하지만, 다른 사람은하지 않습니다. 나는 fmap 유사한 함수를 작성하려고 해요 : 당신이 볼 수 있듯이

transform :: (x -> y) -> Foobar x -> Foobar y 
transform fn foobar = 
    case foobar of 
    Foo1 {} -> foobar {field1 = fn (field1 foobar)} 
    Foo2 {} -> foobar {field1 = fn (field1 foobar)} 
    _  -> foobar 

, 기록 구문은 깔끔하게 내게 필요한 경우에만 fn을 적용, 전체 생성자를 다시 것을 방지 할 수 있습니다. 불행히도 fn이 필요하면 자를 입력해야합니다. 이 경우 (즉, 최종 대안), 표현식은 유형 검사에 실패합니다. 그것은 분명히 내게 확실히 그것은 실패합니다 -하지만 나는 수정 방법에 관해서는 신비 스럽다.

분명히 나는 ​​모든 것을 장시간 쓸 수 있었다. 이것은 잘린 예제를 위해 작동 할 것이지만, 실제로 쓰려는 어플리케이션은 꽤 많이 커집니다. (약 25 명의 생성자, 그 중 일부는 15 개 이상의 필드가 있습니다.)

아무도 내가이 결함을 해결할 수있는 방법에 대한 깔끔한 아이디어가 없습니까?

+0

'파생자 파생 '을 추가하고 컴파일러에서 25 가지 사례를 모두 작성할 수 있습니까? –

+0

@DanielWagner 실제 유형에는 여러 유형 매개 변수가 있습니다. 각 매개 변수에 대해 하나의'transform' 함수를 작성하려고합니다. 나는 그것을 파생시킬 방법을 모른다. – MathematicalOrchid

+0

[이 질문] (http://stackoverflow.com/q/4069840/485115)이 관심의 대상 일 수 있습니다. –

답변

4

하나 솔루션 : (타이핑을 줄이기 위해) 레코드 와일드 카드를 사용하는 것입니다 :

{-# LANGUAGE RecordWildCards #-} 
-- ... 
case foobar of 
    Foo4 {..} -> Foo4 {..} 
+0

우우, 그게 흥미 롭습니다. 또한 내가 원하지 않는 다른 것들을 켠다. 그러나 변환 함수를 정의한 모듈에 이것을 넣으면 안전해야한다. – MathematicalOrchid

2

불행히도 수동으로 패턴 일치를 수행해야합니다. 당신은 너무 많은 일을 피하려면 간단한 수정

{-# LANGUAGE DeriveFunctor #-} 
    data Foo a = ... 
    deriving(Functor) 

이제 우리는 unsafeCoerce

coerceFoo :: Foo a -> Foo b 
    coerceFoo = fmap (error "This shouldn't be used on a phantom type") 

의 안전한 양식을 작성하여 예에서 이것을 사용할 수 있습니다

_  -> coerceFoo foobar 
+0

매개 변수 순서를 뒤집기 위해'newtype'을 사용하면 ... 독창적 인 아이디어. 그게 실제로 작동하는지 궁금해? 나는 그것을 시도해 볼 것입니다 ... – MathematicalOrchid

+0

@MathematicalOrchid Drat Foo에서'b'를 변형시킬 방법이 없기 때문에 그렇지 않습니다. 결코 신경 쓰지 마시길 바랍니다. – jozefg

+0

Aww. :-(오 잘 ... – MathematicalOrchid