问题描述
尝试在 Haskell 中导出用户定义数据的显示时,出现内存不足错误。是什么原因?
--ghc 8.0.2
class X a where
f::a->a
f a = a
data T = AA | BB
instance X T
instance Show T
var::T
var = AA
main = print $ var
我希望 Haskell 能够制作 X 类和 Show 类的 T instacne,结果打印 AA。实例函数适用于类 X(实例 X T),但不适用于类 Show(实例 X Show)。
预期行为是以下代码的行为:
--ghc 8.0.2
class X a where
f::a->a
f a = a
data T = AA | BB deriving Show --notice I changed here
instance X T
--notice I changed here
var::T
var = AA
main = print $ var
-------------------------已编辑--------------------- ---
编译输出为:
$ ghc test.hs -o test
[1 of 1] Compiling Main ( test.hs,test.o )
test.hs:8:10: warning: [-Wmissing-methods]
• No explicit implementation for
either ‘showsPrec’ or ‘show’
• In the instance declaration for ‘Show T’
|
8 | instance Show T
| ^^^^^^
Linking test ...
运行时没有输出:
$./test
当编译的程序运行时,它会侵入我计算机的所有可用内存。当我尝试使用交互式 GHCi 运行它时,它会引发堆栈溢出错误。
为什么可执行文件在尝试像第一个代码示例中那样实例化 T 时尝试分配无限内存?它是一个错误还是其他与函数式编程范式相关的东西?或者一些与类型多态相关的限制?
-------------------------已编辑--------------------- ---
-------------------------OLD--------------------- ---
脚本输出是: code output
GHCi,version 8.6.5: http://www.haskell.org/ghc/ :? for help
[1 of 1] Compiling Main ( outofmemory.hs,interpreted )
outofmemory.hs:9:10: warning: [-Wmissing-methods]
• No explicit implementation for
either ‘showsPrec’ or ‘show’
• In the instance declaration for ‘Show T’
|
9 | instance Show T
| ^^^^^^
Ok,one module loaded.
为什么 ghci 在尝试像第一个代码示例中那样实例化 T 时尝试分配无限内存?它是一个错误还是其他与函数式编程范式相关的东西?或者一些与类型多态相关的限制?
-------------------------OLD--------------------- ---
解决方法
错误输出并未说明内存不足,但我认为您的源文件只是名为 outofmemory
。
关于您的错误:类型类 T
没有实例化必须为其提供实现的任何函数,但 Show
有。您需要实现函数 show
以使您的类型成为 Show
的实例,例如:
instance Show T where
show AA = "AA"
show BB = "BB"
如果您像这样省略 X
的默认实现,您将在 f
中触发相同的错误:
class X a where
f::a->a
-- f a = a -- no default
deriving
关键字允许派生类型类 Read
、Show
、Bounded
、Enum
、Eq
和Ord
。这是由编译器提供的。你不能在你自己的类型中使用它。
当你定义一个实例时,你应该总是定义相关的类方法。如果不这样做,则使用类默认值来填充所需的定义。
事实证明,在许多类中,默认值使用相互递归定义方法。例如。我们可能有以下默认值:
class Eq a where
(==) :: a -> a -> Bool
x == y = not (x /= y)
(/=) :: a -> a -> Bool
x /= y = not (x == y)
我们定义 ==
和 /=
的地方。但是,如果两者一起使用,这些默认值将导致非终止(或内存不足),就像在实例不提供任何定义的情况下一样。事实上,在这种情况下,使用任何一种都会使程序陷入无限(相互)递归。
为了防止这个问题,如果我们没有定义至少一个,GHC 会发出警告,前提是我们启用了警告。
关于您的情况:在类 Show
中,我们还有两个方法,它们的默认值是相互递归的。实际上,在 docs 中我们读到我们必须至少定义其中之一:
最小完整定义
showsPrec | show
即使在这种情况下,如果我们没有定义这些成员中的至少一个,GHC 也会发出警告,前提是我们启用了警告。
这是可以通过启用警告来防止的几个 Haskell 陷阱之一。因此,我强烈建议您始终保持警告状态,例如使用 -Wall
编译器标志。