Errors from data type Show instance

I am working on Gabriel Gonzale's excellent blog post on Free Monads . To help my understanding, I wanted to instantiate Show for the following type and play with it in GHCi, type (from blog post):

data Thread m r = Atomic (m (Thread m r)) | Return r

      

And my Show instance:

instance (Show m, Show r) => Show (Thread m r) where
  show (Atomic m x) = "Atomic " ++ show m ++ " " ++ show x
  show (Return x)   = "Return " ++ show x

      

Unfortunately GHCi is giving me this error when I try to upload a file:

β€’ Expected kind β€˜* -> *’, but β€˜m’ has kind β€˜*’
β€’ In the first argument of β€˜Thread’, namely β€˜m’
  In the first argument of β€˜Show’, namely β€˜Thread m r’
  In the instance declaration for β€˜Show (Thread m r)’

      

So, most importantly: what does this error mean, why am I getting it? I think there is an answer that will help me understand the blog post in a very (albeit slightly roundabout way) way. Also, what would a working implementation of Show look like? I tried to look at an instance for Libo, but didn't understand what was going on.

+3


source to share


2 answers


First of all, the error you received is a kind error. A view is the "type" of a type. Just like types classify values, types classify types. And just as Haskell introduces types from values, it also maps views from types. We use the same symbol ::

to denote that a value is of a certain type (for example, 1 :: Int

) and means that a type is of a certain type (for example, Int :: *

).

The error message mentions two types: *

- the type of types that are populated with values ​​such as Int

and Bool

, a * -> *

- the type of type constructors such as Maybe

, []

and IO

. You can think of type constructors as type-level functions: Maybe

takes a type (of a kind *

) as an argument and returns a type (also of a kind *

) as a result , for example Maybe Int :: *

. Views act the same way as functions; for example Either

has a view * -> * -> *

because it takes two view arguments *

to create a view type *

:

Either :: * -> * -> *
Either Int :: * -> *
Either Int Bool :: *

      

So the error comes from the typeclass constraint on your instance Show

:

instance (Show m, Show r) => Show (Thread m r) where
          ------

      

Show

is a class of view types *

that can be displayed. You can see this by typing :kind Show

(or :k Show

) in GHCi:

> :kind Show
Show :: * -> Constraint

      

So, it Show

takes a view type *

and returns a class constraint. Without going into details about the restrictions, it means that it Show m

implies that m :: *

. However, the definition Thread

passes an argument m

in the definition of its constructor Atomic

, which has a type field m (Thread m r)

. Look at the type Thread

:

> :kind Thread
Thread :: (* -> *) -> * -> *

      

It follows that m :: * -> *

, therefore, a mismatch.

The next mistake is in the implementation of your instance Show

, namely:

show (Atomic m x) = "Atomic " ++ show m ++ " " ++ show x
               -                        ----------------

      

Here you have provided a template that matches multiple fields, but Atomic

only has one field. You have to change the implementation like this:

show (Atomic m) = "Atomic " ++ show m

      

If you remove the constraint Show m

, you will see a more helpful error message:

Could not deduce (Show (m (Thread m r)))
  arising from a use of β€˜show’
from the context (Show r)
  bound by the instance declaration at …
In the second argument of β€˜(++)’, namely β€˜show m’
In the expression: "Atomic " ++ show m
In an equation for β€˜show’: show (Atomic m) = "Atomic " ++ show m

      



This suggests that you are trying to call Show

on a type value m (Thread m r)

, but you do not have this constraint in context. So you can add it:

instance (Show (m (Thread m r)), Show r) => Show (Thread m r) where
          ---------------------

      

This is not "standard" Haskell, so GHC is starting to offer extensions that allow you to:

Non type-variable argument in the constraint: Show (m a)
(Use FlexibleContexts to permit this)
In the context: (Show (m a), Show r)
While checking an instance declaration
In the instance declaration for β€˜Show (Thread m r)’

      

Let's try adding -XFlexibleContexts

(on the command line with ghci … -XFlexibleContexts

, in the session with, :set -XFlexibleContexts

or in the source file with {-# LANGUAGE FlexibleContexts #-}

), since its actually a pretty benign extension. Now we get another error:

Variable β€˜a’ occurs more often than in the instance head
  in the constraint: Show (m a)
(Use UndecidableInstances to permit this)
In the instance declaration for β€˜Show (Thread m r)’

      

We might add -XUndecidableInstances

- all of this means that you are writing a level-level computation that the GHC proof won't stop. This is sometimes undesirable, but in this case it's fine because we know that resolving the instance will either find an acceptable instance Show

or it won't work. Now the compiler accepts it, and we can try our instance Show

, let's say with something simple like m ~ []

and r ~ Int

:

> Atomic [Atomic [Return 1, Return 2]] :: Thread [] Int
Atomic [Atomic [Return 1,Return 2]]

      

Note, however, that this won't work if you set m

in the constructor of a type that has no instances Show

, for example IO

:

> Atomic (return (Atomic (return (Return 1) >> return (Return 2)))) :: Thread IO Int

No instance for (Show (IO (Thread IO Int)))
  arising from a use of β€˜print’
In a stmt of an interactive GHCi command: print it

      

In addition, you may also notice that as a result of your instance, Show

you left out some parentheses:

> Atomic (Right (Atomic (Left "asdf"))) :: Thread (Either String) Int
Atomic Right Atomic Left "asdf"

      

This is an easy fix I leave to you.

This should allow your instance to work with a datatype such as Toy

from the article through Free

:

> Atomic (Free (Output "foo" (Pure (Return "bar"))))
Atomic (Free (Output "foo" (Pure Return ("bar"))))

      

+4


source


According to Thread m r = Action (m (Thread m r) | …

, m

- this is something that takes a type and returns a different type. We call m

a type or view constructor * -> *

. Another example of something similar is Maybe

:

data Maybe a = Just a | Nothing

      

Maybe

itself is not a type. You have to give it another type, for example. Maybe Int

or Maybe String

.

Now Show

expecting a type with a view *

. But Thread

it is required * -> *

. Therefore, GHC is giving up. Since it is Show m

preceded Thread m r

, GHC thinks its a kind *

that doesn't work for Action (m (Thread m r))

, since it would require a type constructor ( * -> *

).



This problem, by the way, explains why type classes exist in some packages Show1

. You can accept this and then write your instance Show

:

instance (Show1 m, Show r) => Show (Thread m r) where
  show (Atomic x) = "Atomic " ++ show1 x
  show (Return x) = "Return " ++ show x

      

Or you can delve into the area of unecidable instances and say what Thread m r

can be shown, if m (Thread m r)

can be shown:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}

instance (Show r, Show (m (Thread m r))) => Show (Thread m r) where
  show (Atomic x) = "Atomic " ++ show x
  show (Return x) = "Return " ++ show x

      

+4


source







All Articles