假设我有如下定义的类型 StrInt
type StrInt = (String, Int)
toStrInt:: Str -> Int -> StrInt
toStrInt str int = (str, int)
Run Code Online (Sandbox Code Playgroud)
我希望 Show 函数如下工作:输入:show (toStrInt "Hello", 123)
输出:"Hello123"
我试图定义 show 如下:
instance Show StrInt where
show (str, int) = (show str) ++ (show int)
Run Code Online (Sandbox Code Playgroud)
但这给了我错误:
Illegal instance declaration for ‘Show StrInt’
(All instance types must be of the form (T t1 ... tn)
where T is not a synonym.
Use TypeSynonymInstances if you want to disable this.)
In the instance declaration for ‘Show StrInt’
Run Code Online (Sandbox Code Playgroud)
关于如何解决这个问题的任何想法?
感谢你的帮助!
你想要做的是 1. 开始不是一个好主意,2. 与已经存在的Show实例冲突,因此没有OverlappingInstanceshackery(这几乎从来都不是一个好主意)是不可能的,以及 3. 错误消息你得到的是不涉及到这些问题; 具有相同消息的其他类实例可能完全没问题,但当然需要 GHC 询问的扩展名。
该Show班是不是产生的任何格式,你觉得看起来不错,现在任意字符串输出。这就是Pretty-printing的目的。Show相反,应该产生语法上有效的 Haskell,就像标准实例一样:
Prelude> putStrLn $ show (("Hello,"++" World!", 7+3) :: (String,Int))
("Hello, World!",10)
Prelude> ("Hello, World!",10) -- pasted back the previous output
("Hello, World!",10)
Run Code Online (Sandbox Code Playgroud)
如果您Show自己编写任何实例,它也应该具有此属性。
再次因为(String, Int)已经有一个Show实例,尽管只是一个来自更通用的实例,即
instance (Show a, Show b) => Show (a,b)
instance Show a => Show [a]
instance Show Int
Run Code Online (Sandbox Code Playgroud)
为相同类型声明一个新实例会导致冲突。从技术上讲,这可以通过使用{-# OVERLAPPING #-}编译指示来规避,但我强烈建议不要这样做,因为当实例分辨率根据类型的呈现方式莫名其妙地发生变化时,这样做会导致非常混乱的行为。
相反,当您确实有充分的理由为包含给定数据的类型提供两个不同的实例时,正确的做法通常是使其成为一个单独的类型(因此很明显会有不同的行为)恰好有相同的组件。
data StrInt' = StrInt String Int
instance Show StrInt' where
...
Run Code Online (Sandbox Code Playgroud)
这实际上编译没有任何进一步的问题或需要扩展。(或者,您也可以使用newtype StrInt = StrInt (String, Int),但这并不能真正为您购买任何东西,只是意味着您不能带入唱片公司。)
表单的实例instance ClassName TypeSynonym 也是可能的,有时也很有意义,但正如 GHC 已经通知您他们需要TypeSynonymInstances扩展或取代它的扩展。事实上TypeSynonymInstances,如果同义词指向像元组这样的复合类型是不够的,在这种情况下,您需要FlexibleInstances(包括TypeSynonymInstances),一个我一直启用的扩展。
{-# LANGUAGE FlexibleInstances #-}
class C
type StrInt = (String, Int)
instance C StrInt
Run Code Online (Sandbox Code Playgroud)