多个重叠实例的交互

问题描述

模块Type.hs定义了同音字词newtype,并且仅导出其类型构造函数,而不导出值构造函数,以免暴露细节。它还提供了构造函数makeType,以平衡缺少值ctor的情况。为什么需要将String换成新类型?因为我希望它不只是String;在我的特定情况下,Type实际上被称为Line,与makeType相对应的内容强制其仅包含一个\n作为最后一个字符。对我来说,newtype似乎是最明显的选择。如果不是这种情况,请原谅我:我正在学习。

module Type (Type,makeType) where

newtype Type = Type String

makeType :: String -> Type
makeType = Type

为了以我喜欢的方式显示类型为Type的值(例如,考虑到我的实际用例Line,我可能想用漂亮的Unicode字符表示\n ,或使用序列<NL>或其他任何内容),我创建了另一个模块TypeShow.hs,后来我 (尝试执行我正在描述的内容)编辑了增加一些实用性。为什么要使用另一个模块?因为我猜想内部事物的工作方式以及在屏幕上显示的方式是两个不同的方面。我错了吗?

{-# LANGUAGE FlexibleInstances #-}
module TypeShow () where

import Type

instance Show Type where
  show = const "Type"

-- the following instance came later,see below why
instance {-# OVERLAPS #-} Show (Maybe Type) where
  show (Just t) = show t
  show _ = ""

除了这对模块(描述了Type的核心以及应如何显示)之外,我还创建了其他类似的对Type1 / Type1ShowType2 / Type2Show,它们都包装了一个String来表示和显示其他类似String的实体。

由于其他原因,我还需要另一个包装可选值的类型,该类型可以是TypeType1类型或任何其他类型,因此我编写了此模块

module Wrapper (Wrapper,makeWrapper,getInside) where

newtype Wrapper a = Wrapper { getInside :: Maybe a }

makeWrapper :: a -> Wrapper a
makeWrapper = Wrapper . Just

(实际上,Wrapper实际上包装了多个Type值,但是我会避免放置更多的细节,这是必要的;如果以下内容很愚蠢,因为我只包装了一个{{1 }}在Type中的值,那么实际上请考虑它包裹了多个。)再次,在这里,我尝试隐藏Wrapper的细节,同时提供Wrapper来制作一个,并且makeWrapper对其内部进行“受控”访问。

我也想在屏幕上显示内容,因此我创建了一个相应的getInside模块,以便WrapperShow.hs的{​​{1}}方法依赖于内容Wrapper方法

show

但是,在这一点上,当类型showmodule WrapperShow () where import Wrapper instance Show a => Show (Wrapper a) where show = show . getInside 时,我想显示a内容,打印一个空字符串而不是Maybe Type,或Wrapper内容;因此我写了我上面评论nothing

鉴于此,Justinstance Show (Maybe Type)都正确显示Type "hello",但Just $ Type "hello"显示Type,就像使用{{1 }}是Wrapper $ Just $ Type "hello"的原始实例,无论事实是,对于Just TypeMaybe)内的这种特定类型,我已经定制了Show实例。 / p>

解决方法

Show的{​​{1}}实例声明中,我们真的不知道Wrapper是什么。但是显然,我们必须已经选择要用于a的{​​{1}}实例。使用可用的信息,唯一匹配的实例是默认实例Show,不需要具体的Maybe a

GHC用户指南in the section about overlapping instances提到了“推迟”实例选择的概念:

这推迟了将哪个实例选择到呼叫站点的问题 对于f,到那时,人们对b类型的了解更多。你可以写这个 如果您使用FlexibleContexts扩展,请自己输入签名。

实例声明本身可能出现完全相同的情况[​​...]解决方案是通过在实例声明的上下文中添加约束来推迟选择

我们可以尝试这种技巧。除了要求Show a => Show (Maybe a),还要求整个 a。现在Show a实例被视为“给定”,我们不做本地决策。我认为这会延迟选择Show (Maybe a)实例来调用像Show这样的站点,在那里我们可以获得有关Show (Maybe a)具体类型的更多信息。

检验这个假设:

print $ Wrapper $ Just $ Type 3

也就是说,我发现这种行为令人困惑,并且会在生产代码中避免这种情况。


正如@dfeuer在注释和链接的代码中指出的,重叠的实例是有问题的。例如,如果我们将此无辜的函数添加到此答案的代码中:

a

模块停止编译:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
newtype Type = Type Int

instance Show Type where
  show = const "Type"

instance {-# OVERLAPS #-} Show (Maybe Type) where
  show (Just t) = show t
  show _ = ""

newtype Wrapper a = Wrapper (Maybe a)

instance Show (Maybe a) => Show (Wrapper a) where
  show (Wrapper edit) = show edit

main :: IO ()
main = print $ Wrapper $ Just $ Type 3
-- output: Type

但是现在我很困惑。为什么原始问题中的实例定义foo :: Show a => Maybe a -> String foo = show 不会发生完全相同的类型错误?


* Overlapping instances for Show (Maybe a) arising from a use of `show' Matching instances: instance Show a => Show (Maybe a) -- Defined in `GHC.Show' instance [overlap ok] Show (Maybe Type) -- Defined at Main.hs:14:27 (The choice depends on the instantiation of `a' 编译失败的原因似乎是实例搜索过程描述中的最后一个要点:

现在找到与以下对象统一的所有实例或作用域内给定的约束 目标约束,但不匹配。 [此处,显示(也许是类型)]这样的非候选对象 目标约束进一步扩大时,实例可能匹配 实例化。如果它们都是不连贯的顶级实例,则 搜索成功,返回主要候选对象。否则搜索 失败。 [...]

最后的项目符号(关于统一实例)使GHC 对于提交重叠的实例比较保守。例如:

f :: [b]-> [b]

假设从f的RHS中我们得到 约束C b [b]。但是GHC不会提交实例(C),因为 在f的特定调用中,b可能被实例化为Int,其中 案例实例(D)仍然更加具体。因此,GHC拒绝了 程序。

也许(与正常功能不同)实例定义不受此特定规则约束,但我看不到文档中提到的内容。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...