Avoid double quotes when deriving Show on newtype

456 views Asked by At

I want to print the internal value of a newtype by deriving Show in the following way, so that I don't have to unwrap Value val every time it needs to be printed.

{-# 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 ()

The idea being that I can simply show hello instead of doing let Value helloVal = hello in show helloVal (a bit of a contrived example but the main point is to avoid unwrapping).

The issue is that this prints the following:

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

Whereas the desired result would be:

hello, world

How to get to the desired output?

3

There are 3 answers

1
Fyodor Soikin On BEST ANSWER

The general convention of Show is that it should produce more or less Haskell code, which, in most simple cases, you could just copy out of GHCi output and plug right back into its input.

This is why show "foo" produces a string "\"foo\"" - with quotes. So that when it's printed in GHCi, you see the quotes. This is also why default derived instance for your type produces "Value \"foo\"" - that's straight up Haskell code that can be compiled and evaluated to produce the original Value value.

But if you really don't want the extra quotes - sure, just convert your Text to String using unpack:

instance Show Value where show (Value v) = unpack v
0
chi On

In the line

show (Value val) = show val

you use show on val which is a Text. This converts val to a String performing quoting and escaping. If you don't want that, use unpack val instead.

Finally, note that without the proper quoting the output of show might be ambiguous when called on, e.g., a list of Value. In such case, if the output of show is [a, b, c] we can't know if the list was of length 3, 2, or 1, since the commas might have been part of the Texts inside.

0
Silvio Mayolo On

To go from Text to String without wrapping in quotes, use unpack.

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

Additionally, note that print is equivalent to putStrLn . show, so you're effectively calling show twice on the value, first on Value and second on String. If you already have a string, you need to use putStrLn rather than print.

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