module TypeClasses where
import Question
typeClasses = do
h2 $ text "Something about type classes"
p <#>Why did we write div, and not /,
in the example with the error message in the beginning
of this document?#>
prompts' [ ("div 4 0", "Program error: divide by zero")
, ("4/0", "Inf")
]
<#>We can see that div and / don't give the same
result. Let's learn some more about these functions.
How?#> ?> do
p <#>We use the command :i (:info):#>
prompts'
[ (":i div"
, "infixl 7 `div`\n\
\div :: Integral a => a -> a -> a -- class member"
)
, (":i /"
, "infixl 7 /\n\
\(/) :: Fractional a => a -> a -> a -- class member"
)
]
p <#>You don't need to worry about the lines including infixl
at the moment.The interesting part is that div and
/ have different types.#>
<#>The function div takes as input two objects of
"Integral" type, i.e. an integer type,
and returns as output an object of integer type.
How does integer division work?#> ?> do
p <#>Did you try it?#>
prompts' [ ("div 4 2", "2")
, ("div 4 3", "1")
, ("div 34 10", "3")
]
p <#>Obviously we can't jump to any conclusions based on
a few examples, but it seems plausible that div
performs integer division. And that is also the case.#>
-- Possibility to use QuickCheck.
<#>Furthermore / works with objects of "Fractional"
type, i.e. different kinds of fractions. How does
division with fractions work?#> ?> do
p <#>We can start by trying the same example as before:#>
prompts'
[ ("4/2", "2.0")
, ("4/3", "1.33333333333333")
, ("34/10", "3.4")
]
<#>During the lecture you learnt that the result of a computation
may vary depending on the type of the result.
Above we didn't state a type, which forces Hugs to guess
what the type should be. In this case there were several
possible types. Usually (as you've already seen) one is
presented with an error message, but in some cases Hugs will
choose the type automatically. In this case Double
was picked. Try with the type Ratio Integer
(integer fraction) instead. The type of an expression is
stated by typing <% tt $ text
" :: " %>.#> ?> do
prompts'
[ ("4/2 :: Ratio Integer", "2 % 1")
, ("4/3 :: Ratio Integer", "4 % 3")
, ("34/10 :: Ratio Integer", "17 % 5")
]
<#>Now we get normal division without the rounding that
comes with floating point types
(e.g. Double). This shows the importance
of stating a type signature. Are there any more reasons?#> ?> do
p <#>See the lecture notes. There is a slide with the heading
"Always Specify Type Signatures!".#>
-- Add a type signature to the temperature function.
p <#>It's time to learn more about what Integral and
Fractional stand for:#>
prompts' [ ( ":i Integral"
, "-- type class\n\
\...\n\
\class (Real a, Enum a) => Integral a where\n\
\ ...\n\
\ div :: a -> a -> a\n\
\ ...\n\
\\n\
\-- instances:\n\
\instance Integral Int\n\
\instance Integral Integer"
)
, ( ":i Fractional"
, "-- type class\n\
\...\n\
\class Num a => Fractional a where\n\
\ ...\n\
\ (/) :: a -> a -> a\n\
\ ...\n\
\\n\
\-- instances:\n\
\instance Fractional Float\n\
\instance Fractional Double\n\
\instance Integral a => Fractional (Ratio a)"
)
]
p <#>Integral and Fractional are
type classes. A certain type can belong to (be an
instance of) different type classes. Above we can see that the
built-in types Int and Integer, two
kinds of integer types, both belong to Integral. Since
Integral is a class for integer types, with operations
such as integer division, this makes sense.#>
p <#>Furthermore, we can see on the last line (even though it may
be hard to understand at this stage), that if
a is an integer type (Integral a),
then the type of fractions with denominators and
numerators of type a (Ratio a) is a
fractional type (Fractional (Ratio a)). The
built-in floating point types Float and
Double are fractional types as well.
Furthermore, we can see that in order for a type to be a
fractional type, it must be a numerical type (Num a
=> Fractional a).#>
<#>What is meant by a numerical type?#> ?> do
prompts'
[ ( ":i Num"
, "-- type class\n\
\infixl 6 +\n\
\infixl 6 -\n\
\infixl 7 *\n\
\class (Eq a, Show a) => Num a where\n\
\ (+) :: a -> a -> a\n\
\ (-) :: a -> a -> a\n\
\ (*) :: a -> a -> a\n\
\ negate :: a -> a\n\
\ abs :: a -> a\n\
\ signum :: a -> a\n\
\ fromInteger :: Integer -> a\n\
\ fromInt :: Int -> a\n\
\\n\
\-- instances:\n\
\instance Num Int\n\
\instance Num Integer\n\
\instance Num Float\n\
\instance Num Double\n\
\instance Integral a => Num (Ratio a)"
)
]
p <#>Numerical types support operations like addition
(+) and multiplication (*), but not
necessarily division. If you read the list
of instances, you will see that all the
types mentioned earlier today, apart from
Boolean values (Bool), are
numerical types.#>
p <#>You will learn more about type classes later in this course,
and you will also define types of your own.#>