Carlos López-Camey
Carlos López-Camey

Reputation: 2515

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?

Upvotes: 6

Views: 221

Answers (3)

C. A. McCann
C. A. McCann

Reputation: 77404

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.

Upvotes: 11

worldsayshi
worldsayshi

Reputation: 1991

Perhaps I'm not following what you are asking for. But the above code could be written like this in order to compile:

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

instance Show A where
  show (Fun f s) = s
  show (Num i) = show i

Some explanation

It looked like you were trying to write a show instance for a constructor (Fun). Class instances are written for the entire data type (there might be exceptions, dunno). So you need to write one show matching on each constructor as part of the instance. Num and Fun are each constructors of the data type A.

Also, deriving can't be used unless each parameter of each constructor is, in turn, member of, in this case, Show. Now, your example is a bit special since it wants to Show (A -> A). How to show a function is somewhat explained in the other responses, although I don't think there is an exhaustive way. The other examples really just "show" the type or some place holder.

Upvotes: 5

leftaroundabout
leftaroundabout

Reputation: 120711

A Show instance (or any class instance) needs to be defined for a data type, not for a type constructor. That is, you need simply

instance Show A where

Apparently, you're trying to get this instance with the deriving, but that doesn't work because Haskell doesn't know how to show A->A. Now it seems you don't even want to show that function, but deriving Show instances always show all available information, so you can't use that.

The obvious, and best, solution to your problem is worldsayshi's: don't use deriving at all, but define a proper instance yourself. Alternatively, you can define a pseudo-instance for A->A and then use deriving:

{-# LANGUAGE FlexibleInstances      #-}

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

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

This works like

Prelude> Fun (const $ Num 3) "bla"
Fun  "bla"

Upvotes: 4

Related Questions