【问题标题】:Why isn’t this newtype being given the right Read instance?为什么这个新类型没有被赋予正确的 Read 实例?
【发布时间】:2017-04-03 10:23:11
【问题描述】:

我从Data.IP 创建了IP 类型的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

(我想让它成为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,行为都是相同的。为什么IPAddressRead 实例与IP 的实例不同?

【问题讨论】:

  • 也给它一个 Show 实例,看看它打印的内容。它会以相同的方式读取。
  • 如果我理解正确,deriving Read 生成的实例的工作方式与IPAddressdata 类型完全相同
  • @amalloy 我将IsString 添加到派生类列表中,然后我能够通过"1.2.3.4" :: IPAddress 创建一个IPAddress。在这个值上调用show 会得到"IPAddress 1.2.3.4",实际上read "IPAddress 1.2.3.4" :: IPAddress 可以满足我的要求。我想你应该把你的评论变成答案!不过,我想解释一下为什么我必须在值前面加上 IPAddress

标签: haskell deriving newtype


【解决方案1】:

GHC 具有三种派生类型类实例的机制:

  • Haskell 标准中概述的normal deriving mechanism,它可以为一组小的预定义类派生实例(EqOrdEnumBoundedReadShow )。
    • 可以使用DeriveFunctorDeriveFoldableDeriveTraversableDeriveLift 扩展来扩展可以派生的类集,这些扩展在启用时与标准中列出的类的处理方式相同。
  • GeneralizedNewtypeDeriving,它可以派生遵循包装类型上的实例的实例。
  • DeriveAnyClass,它将派生类转换为空实例声明。

这里有问题。构建一个场景是非常容易的,其中一个以上的机制可以用于派生一个实例,并且实例可以完全不同!在您的示例中,普通派生 新类型派生都可以应用。如果您还启用了DeriveAnyClass,它也可以应用。

为了消除歧义,GHC 使用您无法更改的硬编码规则,它会从上到下尝试:

  1. 如果可以使用普通派生机制派生类,请使用该机制。
  2. 如果启用了DeriveAnyClass,请使用它。
  3. 如果启用了GeneralizedNewtypeDeriving 并且声明的数据类型是新类型,请使用它。

请注意,这意味着同时打开DeriveAnyClassGeneralizedNewtypeDeriving 实际上毫无价值。如果有的话,应该交换底部的两条规则,但现在不能真正改变。

在您的情况下,由于Read 是一个可以通过普通派生机制为其派生实例的类,因此 GHC 使用该类而不是使用 newtype 派生,并且您会得到您所看到的行为。这与Show 的工作方式也是一致的——派生Show 将产生一个在输出中包含IPAddress 的实例——所以Read 应该遵循相同的格式以满足Read 的一个法则。

如果有一些机制来指示 GHC 使用特定的派生机制,那就太好了,但目前还没有。在这种情况下,您必须手动编写实例。幸运的是,这并不难。

【讨论】:

  • “如果有某种机制来指示 GHC 使用特定的派生机制就好了”。 This is coming in GHC 8.2.
  • @Alec 哇哦,我不知道!这真是太棒了,因为这是我想要有一段时间的功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
  • 2023-02-13
  • 1970-01-01
  • 1970-01-01
  • 2015-03-17
  • 1970-01-01
  • 2021-11-15
相关资源
最近更新 更多