© Stefania Loredana Nita and Marius Mihailescu 2019
Stefania Loredana Nita and Marius MihailescuHaskell Quick Syntax Referencehttps://doi.org/10.1007/978-1-4842-4507-1_4

4. Types

Stefania Loredana Nita1  and Marius Mihailescu1
(1)
Bucharest, Romania
 

In Chapter 3, you saw that GHC contains predefined functions, and you worked a little with the div function. In that example, we subtly introduced types, without a comprehensive discussion. It is time to discuss them now. So, in this chapter, you’ll learn about the main types in Haskell, how you can define your own types, and how the type system in Haskell works.

Basic Types in Haskell

The following are the main types in Haskell:
  • Char represents a Unicode character.

  • Bool represents a Boolean value . There are just two Boolean values: True and False.

  • Int is an integer and represents a bounded type, which means it has a maximum value and a minimum value. These two values depend on the machine. For a 32-bit machine, the minimum value is -2147483648, and the maximum value is 2147483647.

  • The Integer type represents integer values also (very large integers, such as those used in cryptography), but its type is not bounded. Anyway, Int is more efficient.

  • Float represents values of floating-point numbers with single precision.

  • Double represents values of floating-point numbers with double precision.

Next, let’s take a look at the following examples:
Prelude> :t 5
5 :: Num p => p
Prelude> :t 'a'
'a' :: Char
Prelude> :t "abc"
"abc" :: [Char]
Prelude> :t False
False :: Bool
Prelude> :t 3 < 2
3 < 2 :: Bool
Prelude> :t 3.44
3.44 :: Fractional p => p
Prelude> :t pi
pi :: Floating a => a

In this code, the :t command shows the type of the expression that follows the command, the :: sign in the result means has type of,” and the words between the :: sign and the type is called the type signature .

You know that 5 is an integer, but you get Num here. Also, you know that 3.44 and pi are double values, and you get Fractional and Floating, respectively. The reason for this is that Num, Fractional, and Floating are classes in Haskell. For the moment, it is enough to know that Num includes Int, Integer, Double, and Float types; Floating includes Double and Float types; and Fractional includes Double and Float. You will learn more about classes in Chapter 10, and you will see the differences between them.

Another interesting part of the code is [Char]. The square brackets—[]—means that the evaluated expression is a list. In this example’s case, it is a list of Chars, which is equivalent to String.

Next, let’s bind locally a variable with a value. You can do this in two ways: using the let keyword or simply using the name of the variable followed by an equal sign, followed by the value, as shown here:
Prelude> let x = 1
Prelude> a = 'x'
Prelude> :t x
x :: Num p => p
Prelude> :t a
a :: Char
Prelude> :info x
x :: Num p => p  -- Defined at <interactive>:49:5
Prelude> :info a
a :: Char   -- Defined at <interactive>:50:1

In this code, :info is similar to :t, except that it displays some additional information.

Some useful structures are lists and tuples. A list (represented with squared brackets, []) is a collection of elements of the same type (as [Char] showed earlier), while a tuple (represented with parentheses, ()) is a collection of elements of different types. A tuple can be a list, but the reverse is not true. You will learn more about lists and tuples in Chapter 6, but for the moment, let’s see how they look:
Prelude> [1, 2, 3]
[1,2,3]
Prelude> :t ['a', 'b']
['a', 'b'] :: [Char]
Prelude> [1, 'x']
<interactive>:97:2: error:
    • No instance for (Num Char) arising from the literal '1'
    • In the expression: 1
      In the expression: [1, 'x']
      In an equation for 'it': it = [1, 'x']
Prelude> (1, 'x')
(1,'x')
Prelude> :t (1, 'x')
(1, 'x') :: Num a => (a, Char)
Prelude> (1,2,3,4,5)
(1,2,3,4,5)

Defining Your Own Types

Suppose you want to create a structure that simulates a date. You need three integer values corresponding to the day, the month, and the year. You can define it using the data keyword, as shown here:
Prelude> data DateInfo = Date Int Int Int
Prelude> myDate = Date 1 10 2018
Prelude> :t myDate
myDate :: DateInfo
Prelude> :info DateInfo
data DateInfo = Date Int Int Int -- Defined at <interactive>:101:1
DateInfo is the name of your new type, and it is called a type constructor , which is used to refer to the type. The Date after the equal sign is the value constructor (or data constructor), which is used to create values of DateInfo type. The three Ints after Date are components of the type. Note that the name of the type constructor and the name of the value constructor begin with capital letters. If you want to print myDate, you would get an error, because the print function does not have an argument of type DateInfo. To print myDate, you need to modify the definition of DateInfo, adding deriving (Show) to the end of definition (we will talk in detail about deriving in Chapter 10). The procedure is shown here:
Prelude> print myDate
<interactive>:34:1: error:
    • No instance for (Show DateInfo) arising from a use of 'print'
          -- Defined at <interactive>:27:44
    • In the expression: print myDate
      In an equation for 'it': it = print myDate
Prelude> data DateInfo = Date Int Int Int deriving (Show)
Prelude> myDate = Date 1 10 2018
Prelude> print myDate
Date 1 10 2018
The comparison between two dates will give a similar error. You need to add Eq after Show, which tells the compiler that you allow comparison between two dates.
Prelude> data DateInfo = Date Int Int Int deriving (Show, Eq)
Prelude> myDate1 = Date 1 10 2018
Prelude> myDate2 = Date 15 10 2018
Prelude> myDate3 = Date 1 10 2018
Prelude> myDate2 == myDate1
False
Prelude> myDate3 == myDate1
True

Synonyms

Let’s define another type , called StudentInfo. A student is described by name, birth date, and specialization, so StudentInfo looks like this:
Prelude> data StudentInfo = Student String DateInfo String deriving (Show, Eq)
It is a little difficult to distinguish in this definition which is the student’s name and which is the student’s specialization. But you can “rename” the basic types, as shown here:
Prelude> type Birthdate = DateInfo
Prelude> type Name = String
Prelude> type Specialization = String
So, StudentInfo becomes as follows:
Prelude> data StudentInfo = Student Name Birthdate Specialization deriving (Show, Eq)
Prelude> student = Student "Alice Brown" (Date 21 8 1992) "Computer Science"
Prelude> :t student
student :: StudentInfo

This kind of structure is called a product type, and it represents a tuple or a constructor with at least two arguments.

Structures and Enumerations

StudentInfo in this example would be considered a struct in the C/C++ programming languages.

If you want a structure that enumerates the elements, you can do the following:
Prelude> data Color = Red | Green | Blue | Yellow | Purple | Orange deriving (Show, Eq)

In this example, Color is a sum type and represents a type that can have multiple possible forms.

Now, let’s say you want to describe the people in a faculty. These can be teachers, defined by name and subject, or students, defined by name, birth date, and specialization. You can write this as follows:
Prelude> data FacultyPerson = Teacher String String | Student Name DateInfo Specialization deriving (Show, Eq)
Prelude> teacher = Teacher "Emily Brian" "Functional programming"
Prelude> student = Student "James Lee" (Date 23 4 1990) "Computer Science"
Prelude> print teacher
Teacher "Emily Brian" "Functional programming"

Records

Let’s suppose you want to add more information in your StudentInfo type (for simplicity, you will use just Student). The student will be described by first name, last name, birth date, specialization, study year, and average grade. The Student type looks like this:
Prelude> data Student = Student String String DateInfo String Int Float deriving (Show, Eq)

Note

The type constructor now has the same name as the data constructor. Haskell allows you to do this.

Another way other than type to make it more intuitive is to make it a record, as shown here:
Prelude> :{
Prelude| data Student = Student { firstName :: String
Prelude|                        , lastName :: String
Prelude|                        , birthDate :: DateInfo
Prelude|                        , specialization :: String
Prelude|                        , studyYear :: Int
Prelude|                        , averageGrade :: Float
Prelude|                        } deriving (Show, Eq)
Prelude| :}
Prelude> student = Student "Emily" "Brian" (Date 23 4 1990) "Computer Science" 2 9.14
Prelude> firstName student
"Emily"
Prelude> averageGrade student
9.14
Prelude> :t averageGrade
averageGrade :: Student -> Float

In this piece of code, you can see :{ and :}. This means you are writing a command on multiple lines . You can use this just in GHCi, not in .hs files. Using record, you can easily access a field of the structure just by typing the name of the field followed by the name of the variable.

Type System

In Haskell, the system has the following types :
  • Strong type: A strong type system ensures that the program will contain errors resulting from wrong expressions. An expression that meets all the conditions of a language is called well-typed; otherwise, it is ill-typed and will lead to a type error. In Haskell, strong typing does not allow automatic conversions. So, if a function has a Double argument but the user provides an Int parameter, then an error will occur. Of course, the user can explicitly convert the Int value to a Double value using the predefined conversion functions and everything will be fine.

  • Static type: In a static type system, the types of all values and expressions are known by the compiler at compile type, before executing the program. If something is wrong with the types of an expression, then the compiler will tell you, as in the example of lists. Combining strong and static types will avoid runtime errors.

  • Inference type: In an inference type system, the system recognizes the type of almost all expressions in a program. Of course, the user can define explicitly any variable, providing its type, but this is optional.

Summary

In this chapter, you learned the following:
  • Which are the basic types in Haskell

  • How to define your own types

  • How the type system works in Haskell

References

  1. 1.

    M. Lipovaca, Learn You a Haskell for Great Good! A Beginner’s Guide (No Starch Press, 2011)

     
  2. 2.

    C. McBride, “Faking It: Simulating Dependent Types in Haskell,” Journal of Functional Programming, 12(4–5), 375–392 (2002)

     
  3. 3.

    N. Vazou, E. L. Seidel, R. Jhala, D. Vytiniotis, and S Peyton-Jones, S. “Refinement Types for Haskell” in ACM SIGPLAN Notices, vol. 49, no. 9, pp. 269–282 (ACM, 2014)

     
  4. 4.

    J. Hughes, “Restricted Data Types in Haskell,” Haskell Workshop, vol. 99 (1999)

     
  5. 5.

    B. O’Sullivan, J. Goerzen, and D. B. Stewart, Real World Haskell: Code You Can Believe In (O’Reilly Media, 2008)

     
  6. 6.

    Values, types, and other goodies, https://www.haskell.org/tutorial/goodies.html

     
  7. 7.
     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.189.211