Exploring Haskell: Types & Classes

Code examples are adapted from Introduction to Functional Programming course.

Access to GHCi is available on repl.it to test out these snippets online.

Types

Evaluating an expression e.

e :: t -- This reads as e has type t, also used for type casting

Every valid expression has a type, which is calculated using type inference.

To get a type in GHCi use :t which is an abbreviation for :type command.

not False -- True
 
:t not False -- not False :: Bool

Basic Types

Some basic types that are common in other programming languages:

  • Bool - logical values
  • Char - single character
  • String - strings of characters
  • Int - fixed-precision integers
  • Integer - arbitrary-precision integers
  • Float - floating-point numbers
:t True -- Bool - logical values
 
:t 'H' -- Char - single character
:t "Hi" -- [Char] - strings of characters
 
:t 1 -- Num p => p
2^64 :: Int -- is out of the Int range (Overflow)
 
:t 2^65 -- Num p => p
2^65 :: Integer -- 36893488147419103232
 
:t 1.5 -- Fractional p => p

List Types

Lists in Haskell are polymorphic and can only contain a sequence of values with the same type.

:t [False, True, False-- [False, True, False] :: [Bool]
 
:t ['a', 'b', 'c', 'd'-- ['a', 'b', 'c', 'd'] :: [Char]
 
:t [['a'], ['b', 'c']] -- [['a'], ['b', 'c']] -- :: [[Char]]

Tuple Types

Tuples can contain sequence of values with different types

:t (False, True-- (False, True) :: (Bool, Bool)
 
:t (False,'a',True-- (False, 'a', True) :: (Bool, Char, Bool)
 
:t ('a', (False, 'b')) -- ('a', (False, 'b')) :: (Char, (Bool, Char))
 
:t (True, ['a', 'b']) -- (True, ['a', 'b']) -- :: (Bool, [Char])

Functions

A function is a mapping from values of one type to values of another type:

import Data.Char (isDigit) -- Necessary for isDigit to work
:t isDigit -- isDigit :: Char -> Bool
 
:t not -- not :: Bool -> Bool
 
-- Example functions
add (x, y= x + y -- add :: Num a => (a, a) -> a
 
zeroto n = [0..n-- zeroto :: (Num a, Enum a) => a -> [a]

Curried Functions

When a function returns as a result an another function it is called a curried function.

add' x y = x + y -- add' :: Num a => a -> a -> a

Both add and add' produce the same result, where add takes all arguments at the same time, and add' can consume one at a time.

-- Parenthesis in Haskell are right associative and are omitted for brevity.
mult x y z = x * y * z -- mult :: Num a => a -> (a -> (a -> a))

Why is Currying Useful?

Currying makes functions more flexible and allows partial application.

Creating a function that increments by one:

addOne = add' 1
addOne 2 -- 3

Conventions for Currying

To avoid excess parentheses when using curried functions there are two conventions:

  1. The -> in type definition associates to the right.

    Int -> Int -> Int -> Int -- Int -> (Int -> (Int -> Int))
  2. Function application is associated to the left.

    mult x y z -- ((mult x) y) z

Unless explicitly required, all functions in Haskell are defined in the curried form.

Polymorphic Functions

A function can be called polymorphic when its type contains one or more type variables

-- length takes a 'collection' of type 'a' and returns an 'Int'
:type length -- length :: Foldable t => t a -> Int
 
length [False, True-- 2
length [1, 2, 3, 4-- 4
 
-- More Examples
 
:t fst -- fst :: (a, b) -> a
 
:t head -- head :: [a] -> a
 
:t take -- take :: Int -> [a] -> [a]
 
:t zip -- zip :: [a] -> [b] -> [(a, b)]
 
:t id -- id :: a -> a

Overloaded Functions

A polymorphic function is called overloaded if its type contains one or more class constraints.

-- sum takes a list with numeric type 'a', and returns a value of type 'a'.
:t sum -- sum :: (Foldable t, Num a) => t a -> a
 
sum [1, 2, 3-- 6
sum [1.1, 2.2, 3.3- 6.6
sum ['a', 'b', 'c'-- error

Classes

Haskell has a number of type classes:

  • Num - Numeric types
  • Eq - Equality types
  • Ord - Ordered types
:t (+) -- (+) :: Num a => a -> a -> a
 
:t (==) -- (==) :: Eq a => a -> a -> Bool
 
:t (<) -- (<) :: Ord a => a -> a -> Bool

And that’s it for now.

View Other Posts