在新类型上派生 Show 时避免使用双引号

Avoid double quotes when deriving Show on newtype

我想通过以下方式导出 Show 来打印 newtype 的内部值,这样我就不必每次都需要解包 Value val被打印出来。

{-# LANGUAGE OverloadedStrings          #-}

import Data.Text

newtype Value = Value Text
instance Show Value where show (Value val) = show val


main :: IO ()
main = do
    let hello = Value "hello"
        world = Value "world"
    print $ show hello <> ", " <> show world
    pure ()

我的想法是,我可以简单地 show hello 而不是 let Value helloVal = hello in show helloVal(有点人为的例子,但要点是避免展开)。

问题是这会打印以下内容:

"\"hello\", \"world\""

而期望的结果是:

hello, world

如何得到想要的输出?

行中

show (Value val) = show val

您在 val 上使用 show,这是 Text。这会将 val 转换为 String 执行引用和转义。如果您不希望这样,请改用 unpack val

最后,请注意,如果没有正确引用,show 的输出在调用时可能会模棱两可,例如 Value 的列表。在这种情况下,如果 show 的输出是 [a, b, c] 我们无法知道列表的长度是 3、2 还是 1,因为逗号可能是 [=13= 的一部分]在里面。

要从 TextString 而不用引号引起来,请使用 unpack

instance Show Value where
    show (Value val) = unpack val

此外,请注意 print 等同于 putStrLn . show,因此您实际上是在值上调用 show 两次,第一次是 Value,第二次是 String。如果你已经有一个字符串,你需要使用 putStrLn 而不是 print.

putStrLn $ show hello <> ", " <> show world

Show 的一般约定是它应该产生或多或少的 Haskell 代码,在大多数简单的情况下,您可以直接从 GHCi 输出中复制出来并直接插入其输入。

这就是 show "foo" 生成字符串 "\"foo\"" 的原因 - 带引号。这样当它在 GHCi 中打印时,您会看到引号。这也是为什么您的类型的默认派生实例会生成 "Value \"foo\"" - 这是直接的 Haskell 代码,可以编译和评估以生成原始 Value 值。

但如果您真的不想要额外的引号 - 当然,只需使用 unpack:

Text 转换为 String
instance Show Value where show (Value v) = unpack v