Chap 3 | Types and Typeclasses

learnyouahaskell.com

Believe the type

We can get a type of expressions with :t command in GHCi.

ghci> :t 'a'
'a' :: Char

:: is read as "has type of". In the example above, Haskell says "'a' has type of Char". Explicit types are always denoted with the first letter in capital case, for example, Char, Bool, or Int. Square brackets in type name denote a list. [Char] is read as "a list of characters" or "a string". Each tuple has its own type. (Bool, Char) is different from (Char, Char, Char).

Functions also have types. We can optionally give functions an explicit type declaration when defining ones. This is generally considered to be a good practice.

removeNonUppercase :: [Char] -> [Char]
removeNonUppercase st = [c | c <- st, c `elem` ['A'..'Z']]

removeNonUppercase has type of [Char] -> [Char], meaning that it maps from a String to a String. In fact, String is a synonym for [Char] so we can also write the previous example as below.

removeNonUpperCase :: String -> String

For a function that takes several parameters, we do as below.

addThreeInts :: Int -> Int -> Int -> Int
addThreeInts x y z = x + y + z

It takes three Ints, adds them up, and returns the result. There is no explicit distinction between parameters and a return value in a function declaration. They are just separated by ->. Simply the last item in the -> chain is the return value's type.

An overview of some common types
  • Int is an N-bit signed integer on N-bit machines. On a 64-bit system, Int is bounded inside [-9223372036854775808, 9223372036854775807].
  • Integer is also an integer except that it isn't bounded in any range.
  • Float is a single precision floating point number.
  • Double is a double precision floating point number.
  • Bool is a boolean. The only two variants are True or False.
  • Char is a character. It's denoted by single quotes. A list of Char is a String.

Type variables

The built-in function head has type of [a] -> a.

ghci> :t head
head :: [a] -> a

Here, a is a type variable. It isn't a type. (Remember, explicit types are written capital-cased) A type variable describes that it can be of any type. This is like generics in other languages. Functions that have type variables in their declaration are called polymorphic functions. head is polymorphic. It takes a list of any type and returns an element of that type.

Typeclasses 101

Typeclasses are like interfaces in other languages. If a type is a member of a typeclass, the type supports and implements the behavior the typeclass specifies.

In Haskell, == is an infix function and it has type. (Infix functions have to be surrounded by parenthesis when they are not placed between operands)

ghci> :t (==)
(==) :: Eq a => a -> a -> Bool
--     +-------+------+  +----+
--     |Typeclass        |
--             |Parameters
--                       |Return value

Eq a => is a new thing here. A thing placed before => is called a class constraint. The previous declaration means that the == function takes exactly two parameters that are of the same type and returns a value of type Bool. Besides, the two parameters have to be a member of the typeclass Eq. Equivalence has to be defined among the parameter candidates.

Some basic typeclasses
  • Eq is for types that support equality testing. Its members have to implement == and /=.
  • Ord is for types that have an ordering. Its members have to implement >, <, >=, and <=.
  • Show is for types that can be shown to a human. One of the most important functions a value whose type is a member of Show implements is show.
  • Read is sort of the opposite typeclass of Show. The built-in read function takes a String value and returns a value whose type is a part of Read typeclass.
  • Enum members are sequentially ordered types. Values that implements Enum have successors and predecessors, which you can get with succ and pred respectively. Types in this typeclass are (), Bool, Char, Ordering, Int, Integer, Float, and Double.
  • Bounded members have an upper and a lower bound. Built-in function minBound and maxBound have type of Bounded a => a. They take no parameter. They're, so to speak, polymorphic constants. Tuples are bounded if their members are all bounded.
  • Num is a numeric typeclass. For example, a literal 20's type is Num p => p so it is also a polymorphic constant. Int, Integer, Float, and Double are member of Num typeclass.
  • Integral is also a numeric typeclass but it only includes Int and Integer.
  • Floating is also a numeric typeclass but it only includes Float and Double.
Hierarchical structure of numeric typeclasses
f:id:tmsick:20200119182309p:plain
Hierarchical structure of numeric typeclasses

Ref: Haskell教養としての関数型プログラミング - 重城良国 - Google ブックス