6

I am new to Haskell, so my question is probably stupid.

I want a function

show2 :: (Show a) => a -> String

which would return show a for any a, but a if a is itself String. How can I implement it?

P.S. it is great if this function is already implemented somewhere, but I still want to see an example of the implementation.

Prof. Legolasov
  • 649
  • 4
  • 20
  • 4
    This is generally a bad idea, for what it's worth. – Louis Wasserman Mar 26 '15 at 22:26
  • By the way, you may already know this but since you are new: The part that comes before the `=>` is called the context (in your case it's `Show a`). Since there is just one there, you don't have to use parentheses. – MasterMastic Mar 27 '15 at 01:51
  • @LouisWasserman can you explain, why? – Prof. Legolasov Mar 27 '15 at 02:07
  • @Hindsight it's generally inadvisable to want to do any kind of reflectiony logic in Haskell; "exceptions for specific types" and the like are frowned upon. – Louis Wasserman Mar 27 '15 at 02:11
  • 2
    @Hindsight The reason *why* it's frowned upon is that **parametricity** has been found to be extremely valuable property for code to have; this is the idea that if your function is polymorphic in some type variable then it *uniformly* handles all types the same way. This tends to make code much more reusable, composable, and easy to reason about. – Ben Mar 27 '15 at 04:48

3 Answers3

6

You can use cast from Data.Typeable

show2 :: (Typeable a, Show a) => a -> String
show2 s = maybe (show s) id ms
  where ms = cast s :: Maybe String
Lee
  • 142,018
  • 20
  • 234
  • 287
5

You can do this with this piece of dirty and dangerous code:

class Showable a where
  show2 :: a -> String

instance Showable String where
  show2 = id

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

You need -XOverlappingInstances -XFlexibleInstances -XUndecidableInstances to compile and use it.

*Main> show2 "abc"
"abc"
*Main> show2 3
"3"
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
5

The way that Haskell is designed is very much opposed to the concept of an instanceof check. Haskell's design does not incorporate this sort of runtime type check, because Haskell is very focused on strong compilation-time guarantees: a function should not be able to learn the type of its arguments at runtime any more precisely than it knows them at compilation time.

This doesn't meant that the feature doesn't exist in Haskell—Lee's answer demonstrates how to do it—but in Haskell this is an opt-in feature provided by a library, not a core part of the language (unlike a language like Java, where it's a core feature that's always present—you can't opt out of it!).

Note that even in object-oriented programming the instanceof operator is controversial. Many object-oriented programmers very strongly advise against its use. A few examples (out of hundreds):

The advise in all of these tends to be the same: instead of using testing the type of a reference and switching to different behaviors based on it, use polymorphism: define an interface or class that has a method for the operation you want, and have the objects that you were testing with instanceof implement their own versions of that method to do the right thing.

This advice can be translated straightforwardly to Haskell:

  1. Define your own type class to represent the behavior you want
  2. Implement this type class for each of the types that you're interested in, with the correct behavior for each.

So, you could do something like this:

class ToString a where
    toString :: a -> String

instance ToString String where
    toString str = str

instance ToString Integer where
    toString i = show i

-- ...
Community
  • 1
  • 1
Luis Casillas
  • 29,802
  • 7
  • 49
  • 102