Reputation: 37
I have some data that I'd like to print (some is Maybe and some is not), and I'm trying to create a generic showField function as follows:
showField :: (Show a) => a -> Text
showField x
| isJust x = Text.pack $ show $ fromJust x
| isNothing x = "None"
| otherwise = Text.pack $ show x
This is throwing a rigid type error:
• Couldn't match expected type ‘Maybe a0’ with actual type ‘a’
‘a’ is a rigid type variable bound by
the type signature for:
showField :: forall a. Show a => a -> Text
at /data/users/jkozyra/fbsource/fbcode/experimental/jkozyra/hs/holdout_cleanup/HoldoutReaper.hs:244:18
• In the first argument of ‘isNothing’, namely ‘x’
In the expression: isNothing x
In a stmt of a pattern guard for
an equation for ‘showField’:
isNothing x
I generally understand this error, but I don't understand if there's a way to achieve what I'd like to. I've also tried pattern matching rather than guards, but can't quite work that out either. Is there something that I could construct in this format that would work?
Upvotes: 2
Views: 459
Reputation: 1472
The error tells us:
‘a’ is a rigid type variable bound by
the type signature for:
showField :: forall a. Show a => a -> Text
Basically, this tells us that according to the type signature you provided, the type of the first parameter is forall a. Show a
(the 'forall a.
' bit is implied by the signature), which means that the first parameter can be any type that is an instance of Show
. It is a rigid type variable because it is defined by an explicit type signature.
It also tells us:
Couldn't match expected type ‘Maybe a0’ with actual type ‘a’
By applying the functions isJust
and isNothing
— both of type Maybe a -> Bool
— to the first parameter you are also claiming the type of the first parameter is Maybe a
which is obviously not the same type as forall a. Show a => a -> Text
.
You can turn this into a correct program simply by removing the type signature for showField
, but that won't have the behavior you desire — the inferred type signature will be (Show a) => Maybe a -> Text
which obviously only accepts values of Maybe a
(where a
is also an instance of Show
).
In Haskell, you can't have a function that accepts values of both a
and Maybe a
¹. Without more context, it's unclear what your actual goal is, but there is almost certainly a more idiomatic way to achieve it.
¹ Unless you have a type class that has instances for both a
and Maybe a
.
Upvotes: 1
Reputation: 89113
It looks like you're trying to construct an adhoc polymorphic function - a function whose definition varies according to its type.
A parametric polymorphic function does the same thing to all data types:
both :: a -> (a,a)
both a = (a,a)
In Haskell, adhoc polymorphism is implemented using type classes:
class ShowField a where
showField :: a -> Text
instance Show a => ShowField (Maybe a) where
showField Nothing = "None"
showField (Just a) = Text.pack $ show a
However there's no way to define an instance for "all other types other than Maybe a" with type classes, so you just have to define instances for the types you actually care about:
class ShowField Int where
showField = Text.pack . show
class ShowField Float where
showField = Text.pack . show
You can cut down on boilerplate by using -XDefaultSignatures
:
class ShowField' a where
showField :: a -> Text
default showField :: Show a => a -> Text
showField = Text.pack . show
instance ShowField' Int where
instance ShowField' Float where
Upvotes: 11