1

I have the following piece of code

data Showable = forall a . (Show a) => Showable a

instance Show Showable where
    show (Showable a) =
        show a

It works quite fine:

> show (Showable 1)
"1"
> show (Showable True)
"True"

But when it's a string, I get unwanted quotes:

> show (Showable "foo")
"\"foo\""

I know it's because of apply show over a string, so it's the same as:

> show "foo"
"\"foo\""

What I want to do, is when it's a String, use id instead of show.

Something like:

instance Show Showable where
    show (Showable a) =
        case a of
            (String _) -> id a
            _ -> show a

Is it possible? Any workarounds?

caio
  • 1,949
  • 16
  • 20
  • 1
    I would recommend to [follow ErikR's advice](http://stackoverflow.com/a/33586782/1094403) to that recent question (or [mine](http://stackoverflow.com/a/33621574/1094403) which is the same) and just use `String`. Basically, using existential quantification the way you're doing here is no better than just using `String`. – Luis Casillas Nov 19 '15 at 01:39
  • @LuisCasillas you're right. I'll follow your suggestion, but leave the question for further references. Thanks :) – caio Nov 19 '15 at 02:36
  • This has been asked just [so very many times before](http://stackoverflow.com/search?q=[haskell]+string+show+quotes). – Daniel Wagner Nov 19 '15 at 06:00

2 Answers2

1

It is possible to do something along these lines but you need some boilerplate unfortunately, so it would probably be better to go about it in a different way.

Here is one way it could be done though (using something equivalent to a dependent sum):

{-# LANGUAGE ExistentialQuantification, GADTs, DataKinds, TypeFamilies #-}

type family StringP a where
  StringP String = 'True
  StringP a      = 'False

data CheckStringness a where
  IsTypeString  ::                         CheckStringness String
  NotTypeString :: (StringP a) ~ 'False => CheckStringness a

data Showable = forall a. Show a => Showable (CheckStringness a) a

instance Show Showable where
  show (Showable IsTypeString  str  ) = str
  show (Showable NotTypeString other) = show other

The difficult part is that you cannot directly reflect a type into a value in the way that you would want to for this, so you have to write a bit of boilerplate code to take care of that.

Example usage:

ghci> show (Showable NotTypeString (123 :: Int))
"123"
ghci> show (Showable NotTypeString ())
"()"
ghci> show (Showable IsTypeString "abc")
"abc"

Like I said though, I would try to approach the problem in a different way entirely (such as Luis Casillas's and ErikR's recommendations in the comments on this question), to avoid being in this situation in this first place. The main reason I demonstrated this is that things similar to this technique may at some point become nicer to work with and have more practical value than they do now, especially as the dependent Haskell initiative continues.

David Young
  • 10,713
  • 2
  • 33
  • 47
  • how's that different from `data Showable = forall a. Show a => Showable { isString :: Bool, unShowable :: a }` or in fact even just calling `show nonStr` and `str` as appropriate? what compile time "automation" does your approach offer over manual selection? – Erik Kaplun Nov 19 '15 at 08:50
  • @ErikAllik That's a great point. You're right, you don't gain anything here. I got a bit overexcited to apply dependent sums. It does demonstrate the technique (which I believe does have useful applications, especially when GADTs are involved), but this would not be the best application of it. – David Young Nov 19 '15 at 23:18
  • @ErikAllik Actually, I spoke too soon. In order to implement that `Show` instance, the type checker needs to know that `a ~ String` for one of the cases and this is not possible with just a `Bool`. I would still agree that using `show nonStr` and `str` directly would be preferable for this particular example though. – David Young Nov 20 '15 at 01:37
0

I don't know how to do this, but I'm pretty sure this requires some Haskell extensions. First, there is the equality constraint, like (a ~ String). The ~ notation is explained in this article: http://chrisdone.com/posts/haskell-constraint-trick

Also, there is overlapping instances: https://wiki.haskell.org/GHC/AdvancedOverlap

Here is what I would do (untested code, unlikely to compile):

class Show1 a where
  show1 :: a -> String

-- requires some extension since String = [Char]
instance (a ~ String) => Show1 a where
  show1 = id

instance Show a => Show1 a where
  show1 a = show a

instance Show Showable where
  show (Showable a) = show1 a
Ming-Tang
  • 17,410
  • 8
  • 38
  • 76
  • There's a complete overlap between the two instances heads, which is going to cause problems, since contexts are not used during overlap resolution. You could try to change the first to `instance Show1 String`. That would still not help in `Showable`, though. – chi Nov 19 '15 at 08:40