Type System in Haskell

Type System in Haskell

The type system seems to really magical if you are newbie in Haskell. Haskell is a static typed language that works like dynamic. You can ignore type declaration and let the type inference system do it for you. For example,

Prelude> let f a b = a + b
Prelude> f 1 2
Prelude> 3
Prelude> :t f
Prelude> f :: Num a => a -> a -> a

what we do here is defining a function f that takes two parameters a and b and return their sum. We have not declared what are the types of a and b yet, but it works fine!

We can use :t to see the type of f, wow, the type inference system predict the type of function parameters for us. It says that function f takes two parameters a, whose type is Num and return another Number a, really smart! Note that Num here is a type class(different from type actually, we will cover later) in Haskell that represent number like int 1 or float 1.0, etc.

But what if we provide two string instead of number?

Prelude> f "ab" "bb"
<interactive>:17:1:
    No instance for (Num [Char]) arising from a use of f
    In the expression: f "ab" "bb"
    In an equation for it: it = f "ab" "bb"

Ops, an error occurs, saying that the function parameters are not in type Num, instead, they are type [Char](string is just an array of char, like in C++).

Types

Basic Types

We have seen types in above examples, string likes "ab" has type [Char]. Lets see more examples:

Prelude> :t 1
1 :: Num a => a
Prelude> :t 1.0
1.0 :: Fractional a => a
Prelude> :t 'a'
'a' :: Char

We can see that 1 has type Num a, 1.0 has type Fractional a but 'a' has type Char. You may notice that there is an a variable in type Num a and Fractional a, what is the a here? It seems more reasonable if 1 has type Num and 1.0 has type Fractional. But actually, Num and Fractional are type classes which we will see later instead of type like Char. Types in Haskell can be an instance of type classes, the a variable in Num a is a type and it declares that type a is an instance of type class Num. That's enough for now, we will go detail for type classes later.

Create Custom Types

Create type in Haskell is easy, but there is two concept we need to know first.

data Yesorno = Yes | No
isYes :: Yesorno -> Bool
isYes Yes = True
isYes No = False

Here, we use the keyword data to declare a new type Yesorno, the Yes and No on the right is called value constructors in Haskell. To define a type, we need to tell Haskell what are the available values. In the Yesorno example, there are only two values, Yes or No.

In the second line, we declare the type of function isYes , which take a Yesorno type parameter and return a Bool type value.(:: is used to declare type in Haskell, indeed, we can omit the type declaration here because Haskell's smart type inference system will do that for us, but it is always clear to declare ourself)

The next two lines show another concept in Haskell called pattern match. We define the function by defining all the input cases(the good news is we only have two cases here...). If the input parameter is Yes, we return True, if No, we return False. That's all for the Yesorno function. Below shows the function usage.

*Main> isYes Yes
True
*Main> isYes No
False

Type Synonyms

Sometimes we do not need to create new type, suppose we write a program that use the Map from int to string a lot. Note that Map is a type constructor that take two parameters, for example, Map a b defines a type map that map type a to type b. So every time we need to use this kind of map, we need to write Map Int String, a little bit inefficient, right? Luckily, we can use type synonym in Haskell.

import Data.Map
type MapIntString = Map Int String

What we do here is declaring a synonym called MapIntString for type Map Int String, after that, we can use MapIntString instead of Map Int String in later programs, really helpful!

Type Classes

We have meet type classes above, for example, Num and Fractional. Below is the definition of 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

Type class is defined using class keyword, then the type class name Num, following a type variable a(not need to be a, can be b or c...). In addition, it declare some basic functions such as plus, multiply, minus operations, which should be implemented by type.

To declare that a type is an instance of a type class, we can use the keyword instance like below:

instance Num Int

Type Int is declared to be an instance of type class Num. The purpose of type class is to abstract some behaviours as interface. If a type is an instance of type class, it need to confirm with the interface. In the case above, Int need to implement functions declared in Num, so that variable of type Int like 1, 2, 3 can support plus, multiply and minus operations.

There are some others type classes in Haskell, such as Show, Eq. Show is used to turn a value to string, Eq is used to compare values. To make Yesorno type an instance of these type classes, we can also use the keyword deriving.

data Yesorno = Yes | No deriving (Show, Eq)

Prelude> Yes == No
False
Prelude> Yes
Yes

If you are familiar with Python, you can think type class in Haskell is abstract base class(ABC) in Python, type in Haskell is just class in Python.

from abc import ABCMeta

class A:
    __metaclass__ = ABCMeta

    @abstractmethod
    def my_abstract_method(self):
        pass

class B(A):
    def my_abstract_method(self):
        return 'class B'

We can see that the method my_abstract_method in class A is an abstractmethod, which need to be implemented if class B inherit class A. So we can use ABC as an interface, just like that in Haskell.

Current rating: 5

Comments

ADDRESS

  • Email: jimmykobe1171@126.com
  • Website: www.catharinegeek.com