2

I am doing my homework and I have a problem doing an instance of Show and I can't solve it, I tried a lot of things. I copy you the error and my code below.

My code:

type Height = Float
type Width = Float
type Radius = Float
data Rectangle = Rectangle Height Width
data Circle = Circle Radius

class Shape a where
    area :: a -> Float
    perimeter :: a -> Float

instance Shape Rectangle where
    area (Rectangle h w) = h * w
    perimeter (Rectangle b a) = (2*b) + (2*a) 

instance Shape Circle where
    area (Circle r) = pi * r**2
    perimeter (Circle r) = 2*r*pi

type Volume = Float

volumePrism :: (Shape a) => a -> Height -> Volume
volumePrism base height = (area base) * height


surfacePrism ancho largo alto = (2*ancho*largo) + (2*largo*alto) + (2*ancho*alto)


instance Show a =>  Show (Shape a) where 
    show (Rectangle h w) = "Rectángulo de base " ++ w ++ "y altura " ++ h

And what the error says:

The first argument of 'Show' should have kind '*'

3 Answers 3

9

What you want to do is not possible. Shape is a typeclass, not a data type, so it has no constructors you can pattern match on. You could do something like

instance (Shape a) => Show a where
    show shape = "Area: " ++ show (area shape) ++ " Perimeter: " ++ show (perimeter shape)

But this does not seem to be what you want. Instead, you should just define Show for each type individually:

instance Show Rectangle where
    show (Rectangle h w) = "Rectangle with base " ++ show w ++ " and height " ++ show h

instance Show Circle where
    show (Circle r) = "Circle with radius " ++ show r

The error about the "kind" can be quite cryptic for beginners (and sometimes experienced haskellers!), but in this case it's fairly straightforward. This does involve a new concept, though. In Haskell, you have values which have a type, such as functions, constants, even monadic actions. You can also talk about the "type of a type", what is known as the kind. There are a couple that you should know about and be comfortable using: * and Constraint. Most types you'll see only involve * and arrows between them. All "fully applied" data types should have kind *, it basically just means that it doesn't take any type parameters, so

> :kind Int
Int :: *
> :kind String
String :: *
> :kind IO ()
IO () :: *
> :kind Maybe Int
Maybe Int :: *
> :kind Either String Int
Either String Int :: *

However, you can have higher-kinded types as well:

> :kind IO    -- Note lack of ()
IO :: * -> *
> :kind Maybe
Maybe :: * -> *
> :kind Either
Either :: * -> * -> *

Each * just represents another fully applied type. That last detail is important, it means you can't have Either IO Maybe, because that would be a nonsensical type. These can be higher order as well:

> import Control.Monad.State
> :kind StateT
StateT :: * -> (* -> *) -> * -> *

It's the same syntax as with function types, just without kind variables.

The other one you really need to know about is Constraint. This one is specially for typeclasses:

> :kind Show
Show :: * -> Constraint
> :kind Num
Num :: * -> Constraint
> :kind MonadState
MonadState :: * -> (* -> *) -> Constraint

And when fully applied they return a typeclass, rather than a datatype.

In case you're curious, there are GHC extensions that let you work with more complicated kinds, even allowing you to specify the kinds for a type or typeclass. There are some interesting tricks you can do with it, but these are generally considered more advanced features of the type system.

Sign up to request clarification or add additional context in comments.

1 Comment

To amplify bheklilr's laconic "this does not seem to be what you want": You definitely do not want the first instance here, instance (Shape a) => Show a where .... It may look seductively close to what you want, but Haskell Doesn't Work Like That.
4

Shape is a typeclass, and you can’t make a typeclass an instance of another typeclass. (You can make all types that are an instance of a typeclass an instance of a different typeclass as well, but that doesn’t appear to be what you’re trying to do here.)

Rather, you appear to want to implement Show on Rectangle. So say that explicitly:

instance Show Rectangle where 
    show (Rectangle h w) = "Rectángulo de base " ++ w ++ "y altura " ++ h

Comments

2

Your instance declaration is not meaningful. Translated to English it would read as something like "for every a that is an instance of Show, a being an instance of Shape is an instance of Show" or something incomprehensible like that.

Just make Rectangle and Circle instances of Show and leave Shape out of it, unless you want to require that every instance of Shape must be an instance of Show, which is something that you need to put into the declaration of Shape.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.