I don't understand why when I look at the type for example of: a = (1, 2)
I get
a :: (Num a, Num b) => (a, b)
instead, if I look at the type of: b = ('a', 'b')
I get
b :: (Char, Char)
and not something like:
b :: (Char a, Char b) => (a, b)
CodePudding user response:
Haskell has two types of literals: monomorphic and polymorphic. (Though it might be more accurate to say, literals can evaluate to values of monomorphic or polymorphic types.)
'c'
is an example of a monomorphic literal; it is a value of a single type, namely Char
.
> :t 'c'
'c' :: Char
1
, on the other hand, is a polymorphic literal. It can be a value of any type that has a Num
instance.
> :t 1
1 :: Num p => p
> :t 1 :: Int
1 :: Int :: Int
> > :t 1 :: Char
<interactive>:1:1: error:
• No instance for (Num Char) arising from the literal ‘1’
• In the expression: 1 :: Char
Other polymorphic literals include
Floating-point literals
> :t 3.4 3.4 :: Fractional p => p
String literals, when the
OverloadedStrings
extension is enabled.> :t "foo" "foo" :: [Char] > :set -XOverloadedStrings > :t "foo" "foo" :: Data.String.IsString p => p
List literals, when the
OverloadedLists
extension is enabled.> :t ['a', 'b'] ['a', 'b'] :: [Char] > :set -XOverloadedLists > :t ['a', 'b'] ['a', 'b'] :: (GHC.Exts.IsList l, GHC.Exts.Item l ~ Char) => l
Data constructors like
Nothing
(if you want to think of nullary constructors as literals)> :t Nothing Nothing :: Maybe a
Nothing
, for example, can be a value of typeMaybe Int
,Maybe Char
, etc.
CodePudding user response:
Num
is a typeclass, it is a set of types, this looks a bit similar to what an interface is in Java/C#/... The types that are members of the Num
typeclass need to implement a certain set of functions, like in Java where types that implement the interface should implement certain methods. A Char
on the other hand is a type, not a typeclass.
If you write an integer literal, it can be of any type that is a member of the Num
typeclass, since that typeclass has a function fromInteger :: Num a => Integer -> a
that is then called to convert the integer literal to that number type.
If you write a char literal like 'a'
on the other hand, then the only type to which this can be resolved is a Char
.
For string literals, you can use the OverloadedStrings
extension, in that case a string literal can take any type that is a member of the IsString
typeclass.
CodePudding user response:
The point of numerical literals being polymorphic is that you can use them in numerical expressions for wildly different number types.
Prelude> 1 10^12 :: Int
1000000000001
Prelude> pi 10^12 :: Double
1.0000000000031416e12
Prelude> 1/3 10^12 :: Rational
3000000000001 % 3
Prelude> sqrt (-1) 10^12 :: Data.Complex.Complex Double
1.0e12 : 1.0
Prelude> :m Text.LaTeX
Prelude Text.LaTeX> phi 10^12 :: LaTeX
TeXSeq (TeXComm "phi" []) (TeXSeq (TeXRaw " ") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXSeq (TeXRaw "10") (TeXRaw "10")))))))))))))
Mighty useful, because such different numerical types really are used in practice.
But I don't see much point in having something like this for characters, Char
really is the only type that makes sense. (I suppose you could argue that having a Char8
type could sometimes be useful, but this is the Unicode age, a character outside of a string anyways occupies 64 bits, and low-level manipulations should probably be done with Word8
and not use character literals.)
By contrast, for strings it does make a lot of sense to support other types apart from the old [Char]
, that's why -XOverloadedStrings
makes string literals polymorphic.