Haskell 多个重叠实例的交互

Haskell 多个重叠实例的交互,haskell,functional-programming,overlapping-instances,Haskell,Functional Programming,Overlapping Instances,模块Type.hs定义同音词newtype,并仅导出其类型构造函数,而不导出值构造函数,以避免暴露细节;它还提供了一个构造函数makeType,以平衡缺少值的情况。为什么需要将字符串包装为新类型?因为我希望它不仅仅是一个字符串;在我的特定情况下,Type实际上被称为Line,与makeType相对应的内容强制它只包含一个\n,作为最后一个字符。对我来说,newtype似乎是最明显的选择。如果不是这样,请原谅我:我正在学习 模块类型(类型,makeType),其中 newtype=类型字符串 ma

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

模块类型(类型,makeType),其中
newtype=类型字符串
makeType::字符串->类型
makeType=Type
为了以我喜欢的方式显示类型为
type
的值(例如,考虑到我实际使用的
Line
,我可能想用一个漂亮的unicode字符或序列来表示
\n
,或者其他任何东西),我创建了另一个模块
TypeShow.hs
,稍后我将使用它(在尝试做我所描述的事情时)我通过添加一些pragma进行编辑。为什么是另一个模块?因为我猜内部的工作方式和我向屏幕显示的方式是两个独立的方面。我错了吗

{-#语言灵活实例}
模块类型显示()其中
导入类型
实例显示类型在哪里
显示=常量“类型”
--下面的例子是后来出现的,请参见下文
实例{-#重叠{-}显示(可能是类型)其中
show(Just t)=show t
显示
除了这对模块(描述
Type
的核心,以及它应该如何显示)之外,我还创建了其他类似的模块对
Type1
/
Type1Show
Type2
/
Type2Show
,它们都包装了一个
String
,以表示和显示其他类似
String
的实体

出于其他原因,我还需要另一种类型来包装可选值,它可以是
type
Type1
,或者任何其他类型,所以我编写了这个模块

modulewrapper(Wrapper、makeWrapper、getInside)其中
newtype包装器a=包装器{getInside::可能是a}
makeWrapper::a->Wrapper a
makeWrapper=Wrapper.Just

(在实际<代码>包装器< /代码>中实际上包装了不止一个<代码>类型值,但是我将避免添加更多的细节,必要时;如果下面是愚蠢的,因为我在包装器< /代码>中只封装了一个<代码>类型< /代码>值,那么请考虑在实际中包装不止一个。同样,在这里,我试图隐藏

Wrapper
的细节,同时提供
makeWrapper
来制作一个,以及
getInside
来对其内部进行“受控”访问

我还想在屏幕上显示它,所以我创建了一个相应的
WrapperShow.hs
模块,以便
Wrapper
show
方法依赖于内容的
show
方法

模块包装器显示()其中
导入包装器
实例Show a=>Show(包装器a)其中
show=show.getInside
然而,此时,当类型
a
可能类型
时,我想显示
包装的内容
打印一个空字符串,而不是
什么都没有
,或者
的内容只是
;因此我编写了上面注释的
实例show(可能类型)


鉴于此,
Type“hello”
Just$Type“hello”
都正确显示为
Type
,但是
Wrapper$Just$Type“hello”
显示为
Just Type
,就像它使用
Maybe
Show
的原始实例一样,而不考虑
Maybe
中的该特定类型(即
Type
)我已经定制了
Show
实例。

Show
实例声明中,对于
Wrapper
,我们并不知道
a
是什么。但是显然,我们必须已经选择了
Show
实例用于
可能是a
。根据可用的信息,唯一匹配的实例是默认的
showa=>Show(可能是a)
,它不需要具体的
a

GHC用户指南中提到了“推迟”实例选择的概念:

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

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

我们可以尝试这样一个技巧。与其要求
Show a
,不如要求整个
Show(可能是a)
。现在
Show
实例被视为“给定”,我们不会做出局部决定。我想这会延迟
Show(可能是a)的选择
实例调用像
print$Wrapper$Just$type3
这样的站点,在这里我们有更多关于
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
也就是说,我发现这种行为令人困惑,并将在生产代码中避开它


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

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'
但是现在我很困惑。为什么在最初的问题中,实例定义
Show(Wrapper a)
没有发生完全相同的类型错误


编译失败的原因似乎是描述中的最后一个要点