Haskell运行时错误:No instance for (Stacked Int) 问题排查
嘿,我来帮你梳理下代码里的问题——核心是类型不匹配加上类方法的签名设计没贴合栈的操作逻辑,咱们一步步来改:
1. 先看最明显的问题:类方法(/-/)的签名不对
你定义的Stacked类里,(/-/)的签名是Stack [a] -> Stack [a],但作为类方法,它应该和类的类型参数绑定呀!毕竟Stacked a是针对a类型的操作,所以(/-/)应该接收一个a类型的栈实例,再返回新的栈,而不是直接硬编码成Stack [a]。
2. 实例里的类型不匹配是关键错误
你的newtype Stack a = Stack a其实语义有点模糊——如果是栈,通常应该包裹一个元素列表对吧?但你现在的写法让Stack可以包裹任意类型。当你给Stacked (Stack a)写实例时,类里的(/+/)签名会被推导成:
(/+/) :: Stack a -> Stack [Stack a] -> Stack [Stack a]
但你在实现里写Stack (x:xs),这里的xs是[a](来自Stack xs),而x是Stack a,所以x:xs的类型是[Stack a],但如果调用时你传的是Stack [1,2,3](类型是Stack [Int]),就会出现类型完全不匹配的错误——这就是你运行时(其实严格来说编译阶段就该报错,可能是你开了某些GHC扩展?)出问题的根源。
修复后的代码(贴合常规栈逻辑)
咱们调整下类型定义和类的设计,让它符合栈的正常操作(压入元素、弹出栈顶):
第一步:修正Stack类型
让Stack直接包裹元素列表,语义更清晰:
newtype Stack a = Stack [a] deriving (Show)
第二步:调整Stacked类的方法签名
让方法直接针对栈类型和元素类型操作:
class Stacked a where -- 把元素压入栈:栈 -> 元素 -> 新栈 (/+/) :: Stack a -> a -> Stack a -- 弹出栈顶元素:栈 -> 新栈 (/-/) :: Stack a -> Stack a
第三步:实现实例
这时候实例的实现就完全匹配类型了:
instance Stacked a where (/+/) (Stack xs) x = Stack (x:xs) (/-/) (Stack xs) = Stack (tail xs)
测试调用
现在运行就完全正常了:
main = do let myStack = Stack [1,2,3] print $ myStack /+/ 4 -- 输出 Stack [4,1,2,3] print $ /-/ myStack -- 输出 Stack [2,3]
如果你非要保留原始的Stack定义...
如果你坚持用newtype Stack a = Stack a(虽然这种设计不太符合栈的常规语义),那你得保证传入的栈里存的是[Stack a]类型的元素,比如这样调用:
main = do let weirdStack = Stack [Stack 1, Stack 2] print $ Stack 3 /+/ weirdStack -- 输出 Stack [Stack 3,Stack 1,Stack 2] print $ /-/ weirdStack -- 输出 Stack [Stack 2]
但显然这不是你想要的栈操作,所以还是建议按照前面的常规栈逻辑来修改代码~
内容的提问来源于stack exchange,提问作者user7163530

