2017-04-03 100 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哦,哇,我不知道!这听起来很棒,因为这是我现在想要的一个功能。 –

相关问题