Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Showing the type A -> A

data A = Num Int
     | Fun (A -> A) String deriving Show

instance Show (Fun (A -> A) String) where
  show (Fun f s) = s

I would like to have an attribute for a function A -> A to print it, therefore there is a String type parameter to Fun. When I load this into ghci, I get

/home/kmels/tmp/show-abs.hs:4:16:
    Not in scope: type constructor or class `Fun'

I guess this could be achieved by adding a new data type

data FunWithAttribute = FA (A -> A) String 

adding data A = Num Int | Fun FunWithAttribute and writing an instance Show FunWithAttribute. Is the additional data type avoidable?

like image 277
Carlos López-Camey Avatar asked Feb 12 '13 19:02

Carlos López-Camey


1 Answers

Instances are defined for types as a whole, not individual constructors, which is why it complains about Fun not being a type.

I assume your overall goal is to have a Show instance for A, which can't be derived because functions can't (in general) have a Show instance. You have a couple options here:

Write your own Show instance outright:

That is, something like:

instance Show A where
    show (Num n) = "Num " ++ show n
    show (Fun _ s) = s

In many cases, this makes the most sense. But sometimes it's nicer to derive Show, especially on complex recursive types where only one case of many is not automatically Show-able.

Make A derivable:

You can only derive Show for types that contain types that themselves have Show instances. There's no instance for A -> A, so deriving doesn't work. But you can write one that uses a placeholder of some sort:

instance Show (A -> A) where
    show _ = "(A -> A)"

Or even just an empty string, if you prefer.

Note that this requires the FlexibleInstances language extension; it's one of the most harmless and commonly used extensions, is supported by multiple Haskell implementations, and the restrictions it relaxes are (in my opinion) a bit silly to begin with, so there's little reason to avoid it.

An alternate approach would be to have a wrapper type, as you mention in the question. You could even make this more generic:

data ShowAs a = ShowAs a String

instance Show (ShowAs a) where
    show (ShowAs _ s) = s

...and then use (ShowAs (A -> A)) in the Fun constructor. This makes it a bit awkward by forcing you to do extra pattern matching any time you want to use the wrapped type, but it gives you lots of flexibility to "tag" stuff with how it should be displayed, e.g. showId = id `ShowAs` "id" or suchlike.

like image 88
C. A. McCann Avatar answered Sep 23 '22 13:09

C. A. McCann