我有以下课程:
class SappState s where
getTable :: s -> SymbolTable
getStack :: s -> Stack Scope
getScopeId :: s -> ScopeNum
getAst :: s -> Program
putTable :: SymbolTable -> s -> s
putStack :: Stack Scope -> s -> s
putScopeId :: ScopeNum -> s -> s
putAst :: Program -> s -> s
我总是show
data
这个class
实例,其中定义了函数。所以我使用以下代码概括:
instance (SappState s) => Show s where
show st = showT ++ showS ++ showI ++ showA
where
showT = getTable st ++ "\n"
showS = "Scope Stack:\n" ++ getStack st ++ "\n"
showI = "Scope Number:\t" ++ getScopeId st ++ "\n"
showA = getAst st ++ "\n"
但是GHC给了我以下错误:
SappMonad.hs:87:27:
Illegal instance declaration for `Show s'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Show s'
我应该使用FlexibleInstances
pragma吗?我真的不明白它的作用,以及正确的方式,或者我是否应该放弃推广Show
实例。
修改
我激活了pragma,它有这个新错误:
SappMonad.hs:88:10:
Constraint is no smaller than the instance head
in the constraint: SappState s
(Use -XUndecidableInstances to permit this)
In the instance declaration for `Show s'
我也激活了UndecidableInstances
,现在一个简单的deriving Show
将会中断:
data Architecture = Arch
{ archName :: String
, types :: Map.Map DataType Bytes
} deriving (Show)
引发以下错误:
SappMonad.hs:39:17:
Overlapping instances for Show (Map.Map DataType Bytes)
arising from the 'deriving' clause of a data type declaration
Matching instances:
instance (Show k, Show a) => Show (Map.Map k a)
-- Defined in `containers-0.5.0.0:Data.Map.Base'
instance SappState s => Show s -- Defined at SappMonad.hs:89:10
When deriving the instance for (Show Architecture)
第二次修改
我在该主题中搜索了更多内容并找到OverlappingInstances
,添加了编译指示并编译,我认为它正常工作。但我觉得我对这些语言扩展太过分了。 我怎么能停止使用其中一些并仍然获得相同的功能?
答案 0 :(得分:4)
不幸的是,对于这个问题,没有真正干净的解决方案。
您当前方法的最大缺点是,如果程序中的任何地方都有Show
类似的“自动”实例,它们将完全停止工作,您将收到有关“重复实例”的错误。
基本问题是实例解析的工作原理是首先匹配实例声明的右侧。如果匹配,机器将承诺该声明,然后尝试解决左侧所需的任何事情。
因此,在您的情况下,为任何Show Foo
搜索Foo
的实例将无条件地与您的实例匹配,如果找不到SappState Foo
实例,则解析将失败。
OverlappingInstances
稍微减轻了这一点,因为它会首先查找更具体的实例。所以Show Int
将使用普通实例解析,因为它特别提到了Int
。但是如果范围内也有类似instance SappState2 a => Show a
的内容,那么没有更具体实例的任何Foo
都会导致重复的实例错误。
我建议让SappState
的实施者亲自编写Show
个实例。您可以通过提供实用程序函数showSappState :: SappState a => a -> String
来降低成本,以便它们的实例可以只是
instance Show Foo where
show = showSappState
[Show
的正确实施还有一些方法,但适用相同的一般方法]
如果您想确保SappState
的所有实例都是Show
的实例,您可以使用超类来强制执行此操作:
class Show a => SappState a where
...
实际上有一些建议可以自动实现超类,这可以很好地解决你的问题,但是GHC还没有实现。一个例子是IntrinsicSuperclasses
。
为了完整起见,值得一提的是UndecidableInstances
有点危险,因为它可能导致实例解析不能终止。保证它将终止的规则必然有点保守,因为问题是图灵完成,在某些情况下这些需要关闭。
instance SappState a => Show a
的问题在于它将Show a
约束减少为SappState a
约束,并且由于正在搜索的新实例是相同的,因此没有明显的“进展”大小如旧。想象一下如果有人也写了instance Show a => SappState a
会发生什么。