Implement generic show for my Type -> Type

Im just starting out with purescript and would like to implement a generic show for my Type → Type for all values that implement show. What im trying is this:

data Sentiment a = Ask a | Bid a
instance showSentiment :: Show (Sentiment a) where
  show :: forall (a :: Type). Show a => Sentiment a -> String
  show (Bid a) = "Bid " <> show a
  show (Ask a) = "Ask " <> show a

However this does not compile:

  No type class instance was found for

    Data.Show.Show a1


while checking that type forall (a :: Type). Show a => Sentiment a -> String
  is at least as general as type t0 -> String
while checking that expression \$1 ->
                                 case $1 of
                                   (Bid a) -> (...) (...)
                                   (Ask a) -> (...) (...)
  has type t0 -> String
in value declaration showSentimentRecord

where a1 is a rigid type variable
        bound at (line 0, column 0 - line 0, column 0)
      t0 is an unknown type

My questions is now why is this not compiling and is it possible to have a show for my Sentiment type that works for all Int, Number any Record etc?

1 Like

You are very close!

I think you might just be missing one thing: the first line of the instance declaration introduces the type variable a. It’s already in scope inside the body of the instance, where you declare members like show.

(Instance declarations are the one place in PureScript where type variables can be implicitly introduced, as opposed to explicitly with a forall or as parameters on the left side of a type, data, or newtype declaration. Some of us are interested in changing this in some future release, so that instance declarations can, and eventually must, have a forall-style signature as well—as a newcomer getting tripped up on this issue, we would be interested in your feedback on that proposal!)

So:

  • You don’t need to have effectively another type variable shadowing the outer a introduced by the signature of show, and in fact it’s counterproductive to do so; you want the type that show works with to be equal to the type that the instance is defined for, and a new shadowing type variable is not equal to the type it shadows.
  • You want the constraint Show a => to be attached to where a is introduced. You’re implementing a class member, and the type of that member needs to match the declaration of the member in the class. The declaration of show inside class Show t has type t -> String, and so you can’t introduce additional constraints in that type here. But you can introduce constraints at a wider scope than the member declaration—namely, at the instance declaration.

Scroll down for the solution… (you might want to figure it out on your own)

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

instance showSentiment :: Show a => Show (Sentiment a) where
  show :: Sentiment a -> String
  show (Bid a) = "Bid " <> show a
  show (Ask a) = "Ask " <> show a
1 Like

Ok thanks, yes I did try something like that but used forall a. Show a => and got an syntax error. Im to new to giva any feedback since I dont really know what im doing atm, just trial and google :slight_smile: Thanks for a good answer.

For reference I also found this more magic way to do it:

derive instance genericSentiment :: Generic (Sentiment a) _

instance showSentiment :: Show a => Show (Sentiment a) where
  show c = genericShow c
2 Likes

I feel like we could produce a better error here. Maybe the compiler should say something about the type variable a bound in the signature of show not matching the one bound in the instance context? Also, if you originally wrote

instance Show (Sentiment a) where
  show (Ask a) = "Ask " <> show a
  show (Bid a) = "Bid " <> show a

then maybe the compiler could suggest adding the Show instance in the correct place?