【问题标题】:Avoid double quotes when deriving Show on newtype在新类型上派生 Show 时避免使用双引号
【发布时间】:2021-04-29 20:01:18
【问题描述】:

我想通过以下方式导出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

如何获得所需的输出?

【问题讨论】:

  • “正确”的解决方案是始终使用漂亮的打印库(例如 prettyprinter)来实现人类可读的输出,并始终派生 Show。这样,Show 可以用于调试,因为它始终将完整值显示为有效的 Haskell 代码,可以将其输入 GHCi 或使用Read 回读。 Show 的输出可以用pretty-show 格式化,以提高可读性。
  • 感谢您的评论。我明白这有什么意义,但是在这种情况下,我正在使用一个名为 string-interpolate 的库,该库与 Show 一起工作(除其他外)。这里有一个很好的解释它是如何工作的:williamyaoh.com/posts/…
  • @Jivan 但是string-interpolate 完全按照 Jon Purdy 的建议行事;它提供了自己的类型类Interpolatable。您应该使用自定义的漂亮打印机实例化它,并保留编译器生成的默认 Show 实例。事实上,这样的结果会更好,因为你可以直接产生一个Text,而不会引起两次字符串类型的转换。 (并且在您的链接中明确讨论了此类...)
  • @DanielWagner 是的,完全有道理。感谢您指出这一点。

标签: haskell show newtype


【解决方案1】:

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

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

但如果您真的不想要额外的引号 - 当然,只需使用 unpack 将您的 Text 转换为 String

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

【讨论】:

  • 所有(当前)答案都同样值得被接受——我最终接受了这个答案,因为对show 工作原理的额外解释(即生成的 Haskell 代码可以轻松复制/粘贴)
【解决方案2】:

排队

show (Value val) = show val

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

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

【讨论】:

    【解决方案3】:

    要从 Text 转到 String 而不用引号括起来,请使用 unpack

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

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

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

    【讨论】:

      猜你喜欢
      • 2015-04-24
      • 2014-05-03
      • 1970-01-01
      • 2017-08-13
      • 2020-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多