More Comprehensive Information
Most of the time, it's enough to know the type of a value. Sometimes, we may
also want to know about where it is defined and, in case of an operator, its
"fixity" (precedence level and left or right-associativity). To query this
information, we can use :info
or :i
:
>>> :i (+)
class Num a where
(+) :: a -> a -> a
...
-- Defined in ‘GHC.Num’
infixl 6 +
This tells us that the addition operator is a method of the Num
type class.
In particular, if we use addition in any of our functions, then the arguments
to which we want to apply this operator must be of a type that is an instance
of this type class, that implements addition and many other arithmetic
operators. It also tells us that, if a
is an instance of Num
, then (+)
takes two arguments of type a
and returns a result of type a
. The
precedence level of (+)
is 6, which is lower then the precedence level of
(*)
, so we can write arithmetic expressions as we are used to, without
parentheses:
>>> :i (*)
class Num a where
...
(*) :: a -> a -> a
...
-- Defined in ‘GHC.Num’
infixl 7 *
Both (+)
and (*)
are left-associative (infixl
). This means that the
expression 2 + 3 + 4
is evaluated as if it were parenthesized as
(2 + 3) + 4
. The other option is that an operator is right-associative
(infixr
). If addition were right-associative, the expression would be
parenthesized as 2 + (3 + 4)
. For addition, the difference is not important,
but for subtraction, left-associativity is important because we want 3 - 2 - 1
to mean (3 - 2) - 1 = 0
, not 3 - (2 - 1) = 2
.
Finally, the definition of the Num
type class, and of (+)
and (*)
along
with it, is provided in the GHC.Num
module, which is part of the standard
library. What else is defined in the Num
type class?
>>> :i Num
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
{-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
-- Defined in ‘GHC.Num’
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
Ah, okay, so every type a
that is an instance of Num
must implement
addition, subtraction, multiplication, negation, absolute value, sign, and a
conversion from Integer
to type a
. Let's ignore the MINIMAL
line for
now. The last five lines are interesting because they tell us that Word
,
Integer
, Int
, Float
, and Double
are instances of Num
defined already
in the standard library—they are number types, so all of the operations defined
in the Num
type class are available for arguments of theses types.
Getting information about type definitions can also be useful if we want to
write functions that take values of these types as arguments. The function
needs to be able to deal with every possible value of that type, so if we use
pattern matching to do different
things based on different values, we need to know which values there are.
Here, for example, is the definition of the Bool
type:
>>> :i Bool
data Bool = False | True -- Defined in ‘GHC.Types’
instance Eq Bool -- Defined in ‘GHC.Classes’
instance Ord Bool -- Defined in ‘GHC.Classes’
instance Show Bool -- Defined in ‘GHC.Show’
instance Read Bool -- Defined in ‘GHC.Read’
instance Enum Bool -- Defined in ‘GHC.Enum’
instance Bounded Bool -- Defined in ‘GHC.Enum’
This says that there are two values of type Bool
: False
and True
. We'll
learn about type definitions using the data
keyword
later. It also says that Bool
is an instance of
-
Eq
: Two Boolean values can be tested for equality.GHCi>>> False == False True >>> False == True False
-
Ord
: Boolean values can be ordered, withFalse < True
.GHCi>>> False < True True >>> True < False False
-
Show
: Boolean values can be printed in human-readable form.GHCi>>> show False "False" >>> show True "True"
-
Read
: Boolean values can be constructed from the string values"False"
and"True"
.GHCi>>> read "False" :: Bool False >>> read "True" :: Bool True
-
Enum
:Bool
is an enumeration type. Haskell knows how to enumerate all Boolean values in a given range of Boolean values. This is pretty trivial in this case because there are only two such values.GHCi>>> [False .. True] [False,True]
-
Bounded
:Bool
is a bounded type, a type that has a minimum value and a maximum value.GHCi>>> minBound :: Bool False >>> maxBound :: Bool True
I mentioned before that in Haskell, strings are simply lists of characters, and
:info
confirms this:
>>> :i String
type String = [Char] -- Defined in ‘GHC.Base’
The difference here is that String
is defined using type
, not data
, which
makes it a type alias, a type that is completely interchangeable with
[Char]
, the type of lists of characters.