2017-04-03 93 views
2

我創建了一個newtype別名IP類型從Data.IP爲什麼這種新類型沒有被賦予正確的Read實例?

{-# 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 

(我想讓它的ToField一個實例,而無需創建一個孤兒實例。)

新的類型似乎並不儘管如此,應該支持Read。在這種GHCI成績單,你可以看到,給定的字符串可以解釋爲IP但不是作爲一個IPAddress

*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上。爲什麼實例的IPAddressIP的實例不同?

+1

也給它一個Show實例,並查看它打印的內容。它會以相同的方式讀取。 – amalloy

+0

如果我理解正確,那麼由'derived 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'預先給出值。 – bdesham

回答

7

GHC具有用於導出類型類實例三種機制:

  • 在Haskell的標準概述的normal deriving mechanism,它可以導出實例爲一個小的,預定的一組類(EqOrdEnumBoundedRead,和Show)。
    • 該組可衍生可以使用DeriveFunctorDeriveFoldableDeriveTraversable,和DeriveLift擴展,啓用時,被處理過的相同的方式如在標準列出的類被擴展的類。
  • GeneralizedNewtypeDeriving,它可以派生實例,推遲到封裝類型的實例。
  • DeriveAnyClass,它將派生類轉換爲空的實例聲明。

這裏有個問題。構建一個可以使用多個上述機制來派生實例的場景非常簡單,並且實例可能會非常不同!在你的例子中,普通派生新類型派生可以適用。如果您還啓用DeriveAnyClass,它也可以應用。

消除歧義,GHC使用硬編碼的規則,你不能改變,它試圖從頂部到底部:

  1. 如果類可以使用普通的推導機制來獲得,使用。
  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哦,哇,我不知道!這聽起來很棒,因爲這是我現在想要的一個功能。 –

相關問題