2017-04-03 7 views
2

나는 Data.IP에서 IP 유형의 newtype 별칭을 만들어 :이 newtype에 적합한 인스턴스가 주어지지 않는 이유는 무엇입니까?

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

module IPAddress (IPAddress) where 

import Data.IP (IP) 
import Database.PostgreSQL.Simple.ToField 

newtype IPAddress = IPAddress IP 
    deriving (Read, Show) 

instance ToField IPAddress where 
    toField ip = toField $ show ip 

(I 고아 인스턴스를 생성하지 않고 그것을 ToField의 인스턴스를 만들고 싶었다.)

새로운 유형하지 않는 것 그래도 그래야만 Read을 지원할 수 있습니다. 이 GHCi 성적 증명서, 당신은 주어진 문자열이 IPAddress 같은 IP으로 해석하지만 될 수 있음을 볼 수 있습니다

*Main IPAddress> :m + Data.IP 
*Main IPAddress Data.IP> read "1.2.3.4" :: IP 
1.2.3.4 
*Main IPAddress Data.IP> read "1.2.3.4" :: IPAddress 
IPAddress *** Exception: Prelude.read: no parse 

동작에 관계없이 나는에 GeneralizedNewtypeDeriving이 있는지의 동일합니다. IPAddressRead 인스턴스가 IP의 인스턴스와 다른 이유는 무엇입니까?

+1

Show 인스턴스도 제공하고 인쇄 된 내용을 확인하십시오. 똑같은 방식으로 읽 힙니다. – amalloy

+0

올바르게 이해한다면,'deriving Read '에 의해 생성 된 인스턴스는'IPAddress'가'data' 타입 인 것과 똑같은 방식으로 작동합니다 – pyon

+0

@amalloy 파생 된 클래스의리스트에'IsString'을 추가하면, ''1.2.3.4 ":: IPAddress'를 통해'IPAddress'를 생성합니다. 이 값에'show'를 호출하면''IPAddress 1.2.3.4 ''가 나오고 실제로''IPAddress 1.2.3.4 :: :: IPAddress''가 원하는 것을 수행합니다. 나는 당신이 당신의 코멘트를 대답으로 바꾸어야한다고 생각한다! 내가 왜 IPAddress로 값을 prepend해야하는지에 대한 설명을 좋아한다. – bdesham

답변

7

GHC는 typeclass 인스턴스를 도출하기위한 세 가지 메커니즘을 갖는다 : 클래스 (Eq, Ord, Enum, Bounded, Read 작은 미리 정의 된 세트에 대한 인스턴스를 도출 할 수

  • 하스켈 표준에서 설명한 normal deriving mechanism, 및 Show).
    • 사용할 때, 표준에 나열된 클래스와 동일한 방식으로 처리된다 DeriveFunctor, DeriveFoldable, DeriveTraversableDeriveLift 정보를 이용하여 확장 될 수 유도 될 수있는 클래스의 집합.
  • GeneralizedNewtypeDeriving, 이는 랩핑 된 유형의 인스턴스로 지연되는 인스턴스를 파생시킬 수 있습니다.
  • DeriveAnyClass으로, 파생 클래스가 빈 인스턴스 선언으로 바뀝니다.

여기에 문제가 있습니다. 위의 메커니즘 중 하나 이상을 사용하여 인스턴스를 파생시킬 수있는 시나리오를 구성하는 것은 매우 쉽습니다. 인스턴스는 상당히 다를 수 있습니다! 귀하의 예에서는 일반 파생 newtype 파생이 모두 적용될 수 있습니다. DeriveAnyClass도 사용하도록 설정 한 경우에도 적용 할 수 있습니다. 클래스가 일반 유도하는 메커니즘을 사용하여 유도 할 수있는 경우

  1. , 그 사용

    는 GHC는 위에서 아래로 시도하는 사용자가 변경할 수없는 하드 코딩 규칙을 사용하여 명확하게합니다.
  2. DeriveAnyClass이 사용 가능한 경우이를 사용하십시오.
  3. GeneralizedNewtypeDeriving이 활성화되고 선언 된 데이터 유형이 newtype 인 경우이를 사용하십시오.

DeriveAnyClassGeneralizedNewtypeDeriving을 동시에 켜는 것이 효과적이지 않음을 유의하십시오. 어떤 것이 든, 아래 두 규칙은 바꿔야하지만 지금은 바뀔 수 없습니다.

Read은 일반 파생 메커니즘을 통해 인스턴스를 파생시킬 수있는 클래스이므로 GHC는 newtype 파생을 사용하지 않고 인스턴스를 사용하므로 사용자가 보는 동작을 얻습니다. 이것은 Show의 작동 방식과 일치합니다. Show은 출력에 IPAddress을 포함하는 인스턴스를 생성하므로 Read의 한 법칙을 충족하기 위해 Read이 동일한 형식을 따라야합니다.

GHC에 특정 유도 메커니즘을 사용하도록 지시하는 메커니즘이 있으면 좋겠지 만 현재로서는 그렇지 않습니다. 이 경우 인스턴스를 직접 작성해야합니다. 다행히도 그렇게 어렵지 않습니다.

+1

"GHC에 특정 유도 메커니즘을 사용하도록 지시하는 메커니즘이 있다면 좋을 것입니다." [이것은 GHC 8.2에 있습니다.] (https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/DerivingStrategies). – Alec

+0

@Alec 오 와우, 나는 그것에 대해 몰랐다! 그것은 듣기 엔 굉장한 데, 지금까지 내가 원했던 기능이기 때문입니다. –