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 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?