gpt4 book ai didi

haskell - 为什么这个新类型没有被赋予正确的 Read 实例?

转载 作者:行者123 更新时间:2023-12-02 11:00:01 24 4
gpt4 key购买 nike

我从 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 的实例不同?

最佳答案

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

  • normal deriving mechanism Haskell 标准中概述,它可以派生一小部分预定义类的实例(EqOrdEnumBounded 读取显示)。
    • 可派生的类集可使用 DeriveFunctorDeriveFoldableDeriveTraversableDeriveLift 进行扩展code> 扩展,启用后,其处理方式与标准中列出的类相同。
  • GeneralizedNewtypeDeriving ,它可以派生遵循包装类型上的实例的实例。
  • DeriveAnyClass ,它将派生类转换为空实例声明。

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

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

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

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

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

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

关于haskell - 为什么这个新类型没有被赋予正确的 Read 实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43175498/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com