I have the following code:
class ToString a where
toString :: a -> String
instance ToString String where
toString a = a
instance ToString Char where
toString a = [a]
instance ToString Int where
toString a = show a
instance ToString Integer where
toString a = show a
instance ToString Float where
toString a = show a
instance ToString Double where
toString a = show a
I can do toString "Text"
and toString 't'
and both compile fine. But if I do toString 5
I get and error. I am forced to do toString (5::Int)
.
show
does not need a type specified to work. And when I look at the implementations of Show
, I don't see anything magical:
instance Show Int where ...
instance Show Integer where ...
What am I doing wrong that requires me to specify the type and how can I fix it?
UPDATE:
I added {-# LANGUAGE ExtendedDefaultRules #-}
as suggested below, and it worked perfectly. Solved my problem.
You are required to specify a type because 5
is polymorphic in Haskell:
λ> :type 5
5 :: Num a => a
so the compiler doesn't know which Num
instance to choose. However, because of extended defaulting, this does work in ghci:
λ> toString 5
"5"
When you write toString "Text"
or toString 't'
, Haskell is able to tell exactly which concrete type "Text"
and 't'
are: [Char]
(aka String
) and Char
respectively. So it can select an instance and run your code.
toString 5
is subtly different. Numeric literals like 5
are overloaded in Haskell, so that 5
could be an Int
, a Double
, or something completely different with a Num
instance that doesn't have a ToString
instance. So it's not specified which instance of ToString
we should be using (and note that you'll get different observable behaviour with your existing instances based on whether you choose Int
or Double
). Haskell's general response to such a situation is to to report an ambiguous type error, requiring you to do something to pin down the types more.
The authors of the Haskell spec considered that this would be a very common and annoying problem with numeric types in particular, so they built in a mechanism for defaulting ambiguous types, under certain conditions that are fairly conservatively designed to help with numeric literals used with standard Haskell features only.1
A type will only be defaulted if both:
Num
, Floating
, etc)So show 5
works because the constraints on the type of 5
are (Num a, Show a)
; Num
is a numeric class, and all of the constraints are built-in classes.
toString 5
seems like it should be basically the same, but you get the constraints (Num a, ToString a)
, and since ToString
isn't a built-in class defaulting doesn't apply, leaving you with an ambiguous type error.
The extended defaulting rules used in GHCi (or if you use the ExtendedDefaultRules
extension) relax the "only built-in classes" rule (among other extensions), which makes (Num a, ToString a)
eligible for defaulting after all.
The GHC user manual talks about how the usual type defaulting rules are extended in GHCi (or with the ExtendedDefaultRule
s extension), comparing them to the standard rules:
https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#type-defaulting-in-ghci
1 In practice I find it to be an extremely rare problem in actual development (I usually compile with a flag that warns about type defaulting), but that's because I adopt the (common) practice of giving type signatures to almost all top-level functions. When GHC is invoked to compile entire modules written with that practice, there's almost always enough information available to specify all the types and defaulting is never needed.
For example, toString x
would be perfectly fine if it occurred in a function where x
was an argument with type Int
. It would even be fine if x
was a local variable with no explicit type, but it was also passed to a function that required an Int
argument. Or was put in a list along with something else that was known to be an Int
. Etc etc.
But in GHCi you supply expressions to be analysed one-at-a-time, and usually don't bother with optional things like type signatures. Under these conditions numeric literals (and other expressions) are ambiguously typed much more frequently, which is the motivation for GHCi's extended default rules.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With