© Stefania Loredana Nita and Marius Mihailescu 2017

Stefania Loredana Nita and Marius Mihailescu, Practical Concurrent Haskell, https://doi.org/10.1007/978-1-4842-2781-7_2

2. Programming with Haskell

Stefania Loredana Nita and Marius Mihailescu1

(1)Bucharest, Romania

Haskell represents a purely functional programming language that provides many advantages and the latest innovations in the design of programming languages. The most recent standard of Haskell is Haskell 2010; but in May 2016, the Haskell community started working on the next version, Haskell 2020.

The Haskell platform is available for download at https://www.haskell.org/platform/ , where there are other versions of installers and an installation guide. After downloading of the appropriate version, just follow the steps. In this book, we will use version 8.0.1.

This chapter focuses on some of the basic elements that you need to understand before continuing to the next chapters. The information is intended for the users and programmers who already have some experience in Haskell programming.

Functional vs. Object-Oriented Programming

Before starting programming with Haskell, it is important to understand the principles of functional programming (FP), and the similarities and the differences between it and object-oriented programming (OOP). We assume that you have (at least) a basic knowledge of object-oriented programming.

The purpose of OOP and FP is to create programs that are easy to understand, flexible, and have no bugs; but each paradigm has its own approach.

Broadly, the similarities between the two programming paradigms are in the levels of expressive power and the capabilities of encapsulating programs into more compact pieces that could be (re)combined. The main difference is the connection between data and the way operations are applied on that data.

The most important principle of OOP is that the data and the operations applied on that data are closely linked: an object contains its own data and the specific implementation of the operations on the owned data. Thereby, the main model to be abstracted is the data itself. OOP allows you to compose new objects, but also to extend the existing classes through the addition of new methods.

Conversely, the main principle of FP is that functions represent the primary model that should be abstracted, not the data. The implementations of the functions are also hidden (as in OOP), and the abstractions of language are given by the functions and the way they could be combined or expressed. As its name suggests, writing new functions represents the main activity of functional programming .

Language Basics

This section discusses Haskell programming basics, but first you need to understand the components of a Haskell program.

  • The most important level of a Haskell program is a set of modules that allow you to control namespaces and reuse software in big programs.

  • At the top of a module, there are the declarations that define different elements (for example, values, or data types).

  • The next level is represented by expressions, which is static and designates a value. These are the most important elements of Haskell programming.

  • The last level is represented by the lexical structure, which catches the concrete frame of Haskell programs in the text files.

A value is evaluated by an expression, which has a static type. The type system permits defining new data types and more types of polymorphism (parametric or ad hoc).

In Haskell, there are several categories of names.

  • Values. The names for variables and constructors

  • Elements associated with the type system. The names for type constructors, type variables, and type classes

  • Modules. The names for modules

You need to pay attention when naming variables and type variables. These names represent identifiers, which should start with a lowercase letter or an underscore. Other names should begin with an uppercase letter.

As in every programming language, comments are allowed. To comment on a single line, use -- before the comment. A multiline comment begins with {- and ends with -}. The following are examples.

-- This is a single line comment.
{- This is
a multi-line commnet. -}

It is recommended that you already have the WinGHCi window open. The following examples can be implemented and tested in WinGHCi or by opening the command prompt or terminal, depending on your operating system (Windows or Linux). Figure 2-1 and Figure 2-2 show examples of what the windows look like when GHCi is launched. When you open the GHCi (or WinGHCi, for Windows users), you can see two lines of text, as shown in Figure 2-1. The first line gives the module version of GHCi. In the second line, there is Prelude>. What is it about? Prelude represents the standard; it is imported by default in all modules. You can stop importing the Prelude module by enabling the NoImplicitPrelude extension, or by writing a specific import statement for it. The structure and the internal content of the Prelude module can be found at https://www.haskell.org/onlinereport/standard-prelude.html and https://hackage.haskell.org/package/base-4.9.1.0/docs/Prelude.html .

A431532_1_En_2_Fig1_HTML.jpg
Figure 2-1. WinGHCi window
A431532_1_En_2_Fig2_HTML.jpg
Figure 2-2. Launching GHCi from command prompt

Arithmetic

Now that you know a few things about Haskell language programming, let’s do some arithmetic. The following are examples of using the arithmetic operators +, -, *, and /.

Prelude> 5 + 3
8
Prelude> 175 - 23
152
Prelude> 55 * 256
14080
Prelude> 351 / 3
117.0
Prelude> 5 + 3
8
Prelude> 175 - 23
152
Prelude> 55 * 256
14080
Prelude> 351 / 3
117.0

You can combine these operators by using parenthesis. If you want to operate with a negative number, you should use the parenthesis—for example 5 * (-3); otherwise, you will get an error message. Also, there are mathematical functions such sqrt, abs, min, max, and succ.

Prelude> 133 * 18 + 5
2399
Prelude> 133 * (18 + 5)
3059
Prelude> 374 / (20 - 31)
-34.0
Prelude> 3 + sqrt(9)
6.0
Prelude> abs(25-100)
75

Boolean algebra is permitted. True and False represent the two Boolean values. As in other programming languages, && represent the Boolean and, || represents the Boolean or, and the keyword not represents negation. Also, you can test equality by using the == (equal) or /= (not equal) operators.

Prelude> True && False
False
Prelude> False || True
True
Prelude> not True
False
Prelude> (True || False) && (not True)
False
Prelude> 100 == 100
True
Prelude> 100 /= 100
False
Note

The True and False values begin with an uppercase letter.

When you use arithmetic operators or Boolean operators , the left side and the right side of the operator should have the same type; otherwise, you will get an error message .

Prelude> 2 + 2
4
Prelude> "xy" == "xy"
True
Prelude> 2 + "xyz"
<interactive>:26:1: error:
    • No instance for (Num [Char]) arising from a use of '+'
    • In the expression: 2 + "xyz"
      In an equation for ‘it’: it = 2 + "xyz"
Prelude> True && 5
<interactive>:29:9: error:
    • No instance for (Num Bool) arising from the literal '5'
    • In the second argument of '(&&)', namely '5'
      In the expression: True && 5
      In an equation for 'it': it = True && 5
Prelude> 3 == "xy"
<interactive>:30:1: error:
    • No instance for (Num [Char]) arising from the literal '3'
    • In the first argument of '(==)', namely '3'
      In the expression: 3 == "xy"
      In an equation for 'it': it = 3 == "xy"

In the preceding example, the + operator also expects a number on the right side, and the && operator expects a Boolean value on the right side. The equality can be verified only between two items of the same type. The example tests the equality between two strings, which is successful, and between a number and a string, which are different types, so there is an error message. Still, there are some exceptions when you operate with items of different types. This is when implicit conversion occurs. For example, addition using an integer value and a floating-point value is allowed because the integer can be converted to a floating-point number. The following is an example.

Prelude> 3 + 2.5
5.5

Pairs, Triples, and Much More

If you want to set a specific value or expression to a variable, use the keyword let. You do not need to declare the variable before setting a value. In Haskell, once you set a value to a variable, you cannot change that value in the same program. It is similar to a problem in mathematics—a variable cannot change its value in the same problem. The variables in Haskell are immutable. The following advanced example shows that if you set two values to a variable, you will get an error.

Prelude> let x = 4
Prelude> x
4
Prelude> let y = "abc"
Prelude> y
"abc"

Tuples are useful when you know the number of values to be combined. Tuples are marked by parenthesis. Its elements are separated by commas; they are not homogenous and they can contain different types.

Prelude> let pair = (2, "orange")
Prelude> pair
(2,"orange")

As you can see in the preceding example, our tuple is a pair with two elements of different types: a number and a string. Tuples are inflexible, because every tuple with its own size and types actually represents a type itself. Thus, general functions for tuples cannot be written. For example, if you want to add an element to a tuple, you should write a function for a tuple with two elements, a function for a tuple with three elements, and so on. You can make comparisons between tuples only if their components can be compared.

Prelude> let fstTuple = ("apple", 2, True)
Prelude> let sndTuple = ("orange", 3, True)
Prelude> fstTuple == sndTuple
False
Prelude> let trdTuple = ("green", False)
Prelude> fstTuple == trdTuple
<interactive>:53:13: error:
    • Couldn't match expected type ‘([Char], Integer, Bool)’
                  with actual type ‘([Char], Bool)’
    • In the second argument of ‘(==)’, namely ‘trdTuple’
      In the expression: fstTuple == trdTuple
      In an equation for ‘it’: it = fstTuple == trdTuple

There are two important functions, which are applied on a particular type of tuples, namely the pair: fst and snd. Intuitively, fst returns the first element of the pair and snd returns the second element of the pair. In Haskell, you call a function by writing its name, followed by parameters divided by spaces.

Prelude> fst trdTuple
"green"
Prelude> snd trdTuple
False
Prelude> fst (5, True)
5

Lists

Lists are similar to tuples. The main difference between them is that the lists are homogenous data structures; thus, all elements are of the same type. For example, you can have a list of integers, or a list of characters, but you cannot mix them in the same list. Lists are marked by brackets, and the elements are separated by commas. The strings are a list of characters, so the "Haskell" string is actually the list ['H', 'a', 's', 'k', 'e', 'l', 'l']. You can apply different functions on lists. Thus, because strings are lists of characters, you can apply many functions on them.

Prelude> [1, 2, 3] ++ [4, 5]
[1,2,3,4,5]
Prelude> "functional" ++ " programming"
"functional programming"

The ++ represents the concatenation of the left-side list with the right-side list (with elements of the same type). When two lists are concatenated, the left-side list is traversed entirely, and the elements of the right-side list are added at the end of the first list. This could take a while if the left-side list has many elements. Intuitively, adding an element to the beginning of a list is much faster. To add an element at the beginning of the list, use the cons operator ( : ).

Prelude> 1:[2,3,4]
[1,2,3,4]
Prelude> 'A':" flower"
"A flower"
Note

In the second example, the character is between single quotes, and the string is between double quotes.

If you want to extract an element on a particular index, use !!. Pay attention to the chosen index: if it is greater than the number of elements, or if it is negative, you will get an error message. The first index in a list is 0. A list can contain other lists, with the following rule : the lists can have different sizes, but they cannot contain different types.

Prelude> [2,4,6,8,10] !! 4
10
Prelude> [2,4,6,8,10] !! 6
*** Exception: Prelude.!!: index too large
Prelude> [2,4,6,8,10] !! -5
<interactive>:64:1: error:
    Precedence parsing error
        cannot mix ‘!!’ [infixl 9] and prefix `-' [infixl 6] in the same infix expression
Prelude> [[1,2], [3], [4,5,6]]
[[1,2],[3],[4,5,6]]

Lists can be compared if they contain elements that can be compared. The first element of the left-side list is compared with the first element of the right-side list. If they are equal, then the second elements are compared, and so on.

Prelude> [1,2,3] < [5,6,7]
True
Prelude> [1,2] < [-1,6,7]
False
Prelude> [1,2,3] < [-1,6,7]
False

There are many useful functions in lists , such as length , head , tail , init , last , maximum , minimum , sum , product , reverse , take , drop , elem , null , and much more. Figure 2-3 is an intuitive representation of the results of the functions head , tail , init, and last .

A431532_1_En_2_Fig3_HTML.gif
Figure 2-3. A visual representation of the results of functions head, tail, init and last
Prelude> length [1,2,3,4,5]
5
Prelude> head [1,2,3,4,5]
1
Prelude> tail [1,2,3,4,5]
[2,3,4,5]
Prelude> init [1,2,3,4,5]
[1,2,3,4]
Prelude> last [1,2,3,4,5]
5
Prelude> minimum [1,2,3,4,5]
1
Prelude> maximum [1,2,3,4,5]
5
Prelude> reverse [1,2,3,4,5]
[5,4,3,2,1]
Prelude> sum [1,2,3,4,5]
15
Prelude> drop 3 [1,2,3,4,5]
[4,5]
Prelude> take 2 [1,2,3,4,5]
[1,2]
Prelude> elem 6 [1,2,3,4,5]
False
Note

The empty list is []. It is widely used in almost all recursive functions that work with lists. Note that [] and [[]] are distinct things, because the first is an empty list and the second is a non-empty list with one empty list as an element.

Source Code Files

In practice, source code is not written in GHCi; instead, source code files are used. Haskell source code files have the extension .hs. Let’s suppose that you have a file named Main.hs with the following source code.

main = print (fibo 5)

fibo 0 = 1
fibo 1 = 1
fibo n = fibo (n-1) + fibo (n-2)

This source code represents a function that computes the Fibonacci number on a specific index. For the moment, take the function as it is; we will explain it in the next subsection. Let’s recall how to compute the Fibonacci numbers: F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2), where n > 1.

Now, let’s return to the source code files. The file could be saved anywhere, but if the work directory is different from the current directory, you need to change it to that directory in GHCi, using the :cd command, as follows.

Prelude> :cd C:Users

To load a file into GHCi, use the :load command.

Prelude> :load Main

After loading the module, you can observe that the prompt was changed into *Main> to indicate that the current context for expression is the Main module. Now, you can write expressions that include functions defined in Main.hs.

*Main> fibo 17
2584

When loading a module, GHC discovers the file name, which contains, for example, a module M, by looking for the file M.hs or M.lhs. Thus, usually, the name of a module should be the same as the file; otherwise, GHCi will not find it. Still, there is an exception: when you use the :load command for loading a file, or when it is specified invoking ghci, you can provide the file name instead of a module name. The specified file will be loaded if it exists, and it could comprise any number of modules. If you are trying to use multiple modules in a single file, you will get errors and consider it a bug. This is good, if there are more modules with the same M name, in the same directory; you cannot call them all M.hs.

If you forget the path where you saved a source code file, you can find it, as follows.

ghci -idir1:...:dirn

If you make changes in the current source code file, you need to recompile it. The command for recompilation is :reload, followed by the name of the file.

Functions

You have used functions since the beginning of this section; for example, all arithmetic operators are functions with two parameters or the functions applied to lists. Now, it’s time to define our functions. Let’s take the function from the previous subsection. To run a function, you need to write the function’s name, followed by the arguments, which are separated by spaces. Parentheses for arguments are not needed.

You haven’t used the return keyword. This is because Haskell does not have a return keyword; a function represents a single expression, not a succession of statements. The outcome of the function is the worth of the expression. Still, Haskell has a function called return, but it has a different meaning than in other programming languages.

Let's write a simple function that computes a power.

Prelude> pow a b = a ^ b
Prelude> pow 2 10
1024

A function has a type, which could be discovered using the :type command.

Prelude> :type pow
pow :: (Num a, Integral b) => a -> b -> a

As secondary effect , a dependence on the global state and the comportment of a function is introduced. For example, let’s think about a function that works with a global parameter, without changing its value and returning it. When a piece of code changes the value of the variable, it affects the function in a particular way, which has a secondary effect; although our function does not change the value of the global variable, which is treated as a constant. If a variable is mentioned out of scope, the value of the variable is obtained when the function is defined.

The secondary effects are usually invisible outcomes for functions. In Haskell, the functions do not have secondary effects, because they are depending only on explicit arguments. These functions are called pure. A function with side effects is called impure.

Prelude> :type writeFile
writeFile :: FilePath -> String -> IO ()

The Haskell type system does not allow combining pure and impure code.

if-else

As in other programming languages, Haskell also has the if-else statement . Its syntax is very simple. Let’s write a function that returns the maximum between two values.

Prelude> maximum a b = if a > b then a else b
maximum :: Ord t => t -> t -> t
Prelude> maximum 5 6
6

An important aspect of Haskell is indentation. You need to pay attention to how you organize your code. For example, if you write the maximum function in an .hs file, then it should look like the following.

maximum a b = if a > b
                          then a else b

Now, let’s return to our function. The word maximum represents the name of the function, and a and b are the parameters. The = after the parameters suggests that the implementation of the function is next. Then, you compare the two parameters and return the greatest of them.

The if-else statement has three elements:

  • A predicate, which represents the Bool expression which follows the if keyword

  • The then keyword, which is followed by an expression, and evaluated if the expression that follows the if statement is True

  • The else keyword, which is followed by another expression, and evaluated if the expression that follows the if statement is False

The two expressions that follow then and else are called branches. They should be the same type; otherwise, it will be cancelled by the compiler/interpreter. The following example is wrong because 10 and abc have different types.

If expression then 10
              Else "abc"

In Haskell, the if-else statement without an else statement is not allowed, because it is a programming language based on expressions.

Types

Variables represent names for expressions. When a variable is linked to an expression, the expression cannot be changed into the current scope, but you are allowed to use the variable, instead of the linked expression. In other word, a variable identifies a location of memory, which could have different values at different times.

Simple vs. Polymorphic Types

A simple value is a value that has only one type. This is discussed more about types in the “Data Types” section.

A polymorphic value is that value which could have multiple types. It is a very useful and used feature of Haskell. There two main types of polymorphism: parametric and ad hoc.

Parametric Polymorphism

Parametric polymorphism occurs if the variable has more than one type, such that the its value could have any type resulted from replacing the variable with explicit types. This means that there are no constraints regarding type variables. The simplest and the easiest example is the identity function.

Identity :: a -> a

The a can be replaced with any type, whether it is a simple type, such as Int, Double, Char, or a list, for example. So, for a, there is no constraint regarding the type.

The value of a parametrically polymorphic type has no knowledge of type variables without constraints, so it should act in the same way regardless of its type. This fact is known as parametricity, and it is very useful, even if it is limiting .

Ad hoc Polymorphism

Ad hoc polymorphism occurs when a variable chooses its type according to its behavior at a particular moment, because it is an implementation for every type. A simple example is the + function. The compiler should know if it is used to add integers, or two floating-point numbers.

In Haskell, ambiguity is avoided through the system of type classes or class instances. For example, to compare two objects, you need to specify how the == operator behaves. In Haskell, the overloading is extended to values; for example, a lowerLimit variable could have different values according to its use. If it refers to an Int, its value cloud is –2147483648, and if it refers to Char, its value could be 'NUL'.

Type Classes

In Haskell, you identify the following aspects of type: strong, static, and automatically inferred.

The strong type system of Haskell assures the fact that a program does not contain errors obtained from expressions without a meaning for compiler.

When a program is compiled, the compiler knows the value of every type before the code is executed. This is assured by the static type system. Also, when you write expressions of different types, you will get an error message. By combining the strong type and the static type, the type errors will not occur at runtime.

Prelude> 2 + "5"
<interactive>:28:1: error:
    • No instance for (Num [Char]) arising from a use of '+'
    • In the expression: 2 + "5"
      In an equation for 'it': it = 2 + "5"

At compilation, each expression’s type is known. If you write an addition symbol between a number and a Boolean value, you will get an error message. In Haskell, each thing has a type. A benefit of Haskell is that it has the type inference, through which the type is implicitly inferred. So, if you write a number, you do not need to specify that it is a number.

Strong and static types bring security, while inference makes Haskell concise, so you have a safe and an expressive programming language.

Function Types

In Haskell, the functions have types. When you write functions, you can give them an explicit declaration, a fact that is helpful when you deal with complex functions. If you want to declare a function, you proceed as follows.

getMax :: [Int] -> Int

The meaning is intuitive. In the preceding example, the getMax function returns the maximum value from a list of integers. The :: symbol is followed by the domain of the definition of the function, and the -> shows the type of the result.

If you have a function with more parameters, you proceed as follows.

addition :: Int -> Int -> Int

Here, you have the addition function, which computes the sum of the two integers. The first two Ints show the type of the parameters, and they are divided by the -> symbol, and the last Int shows the result type.

It is recommended that functions be written with the explicit declaration; but if you are not sure about that, you can just write the function, and then check its type using the :t or :type commands.

Data Types

The following are some basic data types .

  • Int: Integer numbers, which are signed and have fixed width. The range of values is not actually fixed; it depends on the system (32/64 bits), but Haskell assures that an Int is larger than 28 bits.

  • Integer: Integer numbers with unbounded dimensions. The Integer type consumes more space and is less performant than Int, but it brings more dependably correct answers. In practice, it is used less than Int.

  • Double: Floating-point numbers. Usually, the double value is represented in 64 bits, using the natural floating-point representation of the system.

  • Char: Unicode character.

  • Bool: A value from Boolean algebra. There are only two possible values: True and False.

Different from other programming languages , Haskell does not explicitly have the data type string. Strings are actually lists of characters.

If you want to check the type of value or an expression, you simply use the :t command, followed by the value or expression, as follows.

Prelude> :t 5.3
5.3 :: Fractional t => t
Prelude> :t "abcd"
"abcd" :: [Char]
Prelude> :t (5 < 2)
(5 < 2) :: Bool

Input/Output (IO) Mechanisms

GHCi accomplishes more things than evaluating straightforward expressions. When an expression has the type IO a, for some a, GHCi is executing it like an IO computation. Basically, a value which has type (IO a) represents an action that when it is executed, its result has type a.

Prelude> length "Haskell"
7
Prelude> 100/2
50.0

When an expression’s type is general, it is instantiated to IO a.

Prelude> return True
True

The result of an expression’s evaluation is printed if

  • It represents an instance of Show

  • Its type is not ()

In order to understand the following example , it is necessary to understand do notation. By using do, the notation (instruction) represents an alternative to the monad syntax.

The following example implies IO and you refer to the computation’s values as actions. It’s important to mention that do is applied with success with any monad.

The >> operator works in the same way as in do notation. Let’s consider the following example, which is formed from a chain of different actions.

putStr "How" >>
putStr " " >>
putStr "is with programming in Haskell?" >>
putStr " "

The following example can be rewritten using do notation.

do putStr "How"
        putStr " "
        putStr "is with programming in Haskell?"
        putStr " "

As you can see, the sequence of instructions almost matches in any imperative language. In Haskell, you can connect any actions if all of them are in the same monad. In the context of the IO monad, the actions that you are implementing could include writing to a file, opening a network connection, and so forth.

The following is a step-by-step translation in Haskell code.

do action1
        action 2
        action 3

becomes

action1 >>
do action2
        action3

and so on, until the do block is empty.

Besides expressions, GHCi takes also statements, but they must be in the IO monad. Thus, a name could be bounded to values or a function for further use in different expressions or statements. The syntax of a statement is the same as the syntax of do expressions.

Prelude> x <- return "abc"
Prelude> print x
"abc"

The preceding statement, x<-return "abc" could be “translated,” so execute return “abc” and link the outcome to variable x. Later, the variable could be used in other statements; for example, for printing.

When -fprint-bind-result is enabled, the outcome of a statement is typed if

  • the statement does not represent a binding, or it is a binding to a single variable (v <- e).

  • the type of the variable does not represent polymorphism, or (), but it represents a Show instance.

The binding could also be done using the let statement .

Prelude> let y = 10
Prelude> y
10

A characteristic of the monadic bind is that it is strict; namely, the expression e is evaluated. When using let, the expression is not instantly evaluated.

Prelude> let z = error "This is an error message."
Prelude> print z
*** Exception: This is an error message.
CallStack (from HasCallStack):
  error, called at <interactive>:18:9 in interactive:Ghci8

Another important thing is that you can write functions directly at the prompt.

Prelude> f x a b = a*x + b
Prelude> f 3 5 2
17

Nevertheless, this will be a little awkward when you are dealing with complex functions, because the implementation of the function should be on a single line.

Prelude> f a | a == 0 = 0 | a < 0 = - a | a > 0 = a
Prelude> f 100
100
Prelude> f (-324)
324
Prelude> f 0
0

But Haskell comes with a solution. The implementation could be divided into multiple lines, using the :{ and :} commands.

Prelude> :{
Prelude| f a
Prelude| | a == 0 = 0
Prelude| | a < 0 = -a
Prelude| | a > 0 = a
Prelude| :}

If an exception occurs while evaluating or executing statements, they are caught and their message is typed, as you have seen in previous examples.

The temporary binds can be used the next time you load or reload a file, because they will be missed after (re)loading, but can be used after the :module: command (discussed in the next section) when it goes to another location. If you need to know all of the binds, you can use the following command.

Prelude> :show bindings
x :: [Char] = "abc"
y :: Num t => t = _
it :: Num t => t = _
z :: a = _

If +t is enabled, you can find every variable’s type.

Prelude> :set +t
Prelude> let (x:xs) = [1..]
x :: Integer
xs :: [Integer]

There is another possibility that GHCi identifies when a statement is not terminated, and permits writing on multiple lines. It is enabled by the :set +m command. The last line is empty to mark the end of multiple line statement.

Prelude> :set +m
Prelude> let a = 100
Prelude|

If you want to bind values to more variables in the same let command, you should proceed as follows:

Prelude> :set +m
Prelude> let a = 154887
Prelude|     b = 547
Prelude|

For multiple line statements , you can use do. The individual statements are separated by semicolons. The right brace marks the end of the multiline statement.

Prelude> do {
Prelude| print b
Prelude| ;print b^ 2
Prelude| }
547
299209
Prelude>

The multiple-line statements could be interrupted as follows.

Prelude> do
Prelude| print a
Prelude| ^C
Prelude>

Haskell has a special variable, called it, which receives the value of an expression typed in GHCi (of course, the expression should not be a binding statement).

Prelude> 2*3
6
Prelude> it+5
11

You need to remember that an expression must have a type, instantiated from Show; otherwise, you will get an error message.

Prelude> head

<interactive>:14:1: error:
    • No instance for (Show ([a0] -> a0)) arising from a use of 'print'
        (maybe you haven't applied a function to enough arguments?)
    • In a stmt of an interactive GHCi command: print it

Note that the it variable changes its value every time a new expression evaluation occurs, and the previous value is missed.

Haskell permits the use of command-line arguments through the getArgs function, which is passed to the main using the :main command .

Prelude> main = System.Environment.getArgs >>= print
Prelude> :main abc xyz
["abc","xyz"]


Prelude> :main abc "xyz pq"
["abc","xyz pq"]
Prelude> :main ["abc", " xyz pq "]
["abc","xyz pq"]

Any function could be called using -main-is or the : run commands .

Prelude> abc = putStrLn "abc" >> System.Environment.getArgs >>= print
Prelude> xyc = putStrLn "xyz" >> System.Environment.getArgs >>= print
Prelude> :set -main-is abc
Prelude> :main abc "xyz pq"
abc
["abc","xyz pq"]
Prelude> :run xyz ["abc","xyz pq"]
xyz
["abc","xyz pq"]

Modules

This section introduces the notations and information necessary for understanding the rest of the book.

It’s important to acknowledge the fact that a module in Haskell serve in two directions with a specific purpose: controlling namespaces and creating abstract data types. This aspect is very important in understanding complex applications that are developed in a cloud and big data environment.

If you are looking at a module from a technical perspective, a module is a very big declaration that starts with module keyword. Consider the following example, which presents a module called Tree.

module Tree (Tree(Leaf, Branch), fringe) where

data Tree a                       = Leaf a | Branch (Tree a) (Tree a)

fringe :: Tree a -> [a]
fringe (Leaf x)    = [x]
fringe (Branch left right) = fringe left ++ fringe right

The following example is presented from official documentation available at https://www.haskell.org/tutorial/modules.html , which has the best explanation. It can be used as a prototype for different tasks that can be implemented into a distributed environment. An important operation in the preceding example is the ++ infix operator, which concatenate the two lists, left and right. In order for the Tree module to be imported into another module, follow this code snippet.

module Main (main) where
import Tree (Tree((Leaf, Branch), fringe)


main = print(fringe(Branch(Leaf 1)(Leaf 2)))

The various items that are imported into and exported outside of the module are called entities. Observe the explicit import list in the declaration of the import. If you omit this, it would cause all the entities that are exported from Tree to be imported.

:load/:reload

As you already know, the default module of Haskell is Prelude. You can change it by importing another module using the :load command. When it is appended, it automatically imports into the scope corresponding to the last loaded module. To be certain that GHCi imports the interpreted version of a module, you can add an asterisk when you load it, such as :load *modulename.

Prelude> :load Main.hs
Compiling Main             ( Main.hs, interpreted )
*Main>

Now, you can use expressions that involve elements from the loaded module. The star that accompanies the name of the module shows that the module represents the full top-level scope of the expressions from the prompt. In the absence of the asterisk, only the module’s exports are observable.

:module

The :module command also permits scope manipulation. If the module keyword is accompanied by +, then the modules are added; if it is accompanied by -, then the modules are removed. If there is no + or -, the actual scope is substituted by the modules after the :module command.

The use of module brings more benefits than using the import command. The main facilities are

  • the full top-level of a scope is opened through the * operator.

  • the unneeded modules can be removed.

:import

The :import command is used to add modules.

Prelude> import Data.Char
Prelude> toUpper 'a'
'A'

To see all imported modules, use the :show imports command.

Prelude> import System.IO
Prelude> :show imports
import Prelude -- implicit
import Data.Char
import System.IO

As you can see, Prelude is imported implicitly, but it could be replaced with the explicit Prelude import.

When there are more modules in the scope, it name conflicts could rise. In this case, Haskell will alert you.

As you have seen, there are two ways to get a module in the scope: by using :module/import or by using :load/:reload. Still, there are significant differences between the two approaches in the collection of the modules.

  • The collection of modules that are loaded. changed using :load/:reload and viewed using :show modules.

  • The collection of modules in the scope at the prompt. changed using :module/import, and instantaneous after :load/:reload, and viewed using :show imports.

A module can be added into the scope (through :module/import) if

  • the module is loaded.

  • the module belongs to a package known by GHCi.

In any other cases, it will be an error message.

Operators Used as Sections and Infix

In Haskell, there are two types of notations used for function calling: prefix notation and infix notation. Usually, prefix notation is used: the name of the function, followed by the arguments. The infix notation is where the name of the function stands between its arguments. Note that the infix notation can be used for functions with two parameters. If the function has more than two parameters, then the infix notation becomes inconvenient and difficult to follow.

The best-known infix functions are operators. As you well know, the arithmetic operators take two arguments, so they are by default infix. However, if you begin the line with an arithmetic operator in parenthesis, followed by the two arguments, then it becomes a prefix function.

Prelude> (+) 4 5
9
Prelude> (*) 10 10
100

Anyway, there is a way to use infix notation for an ordinary function. This is done by putting the name of the function between the ` symbol, as follows.

Prelude> let concatPrint x y = putStrLn $ (++) x y
 Prelude> concatPrint "a" "b"
 ab
 Prelude> "a" `concatPrint` "b"
 ab

There is a particular language structure for an incomplete application on infix operators. Broadly, when you use an operator, it behaves like a function that receives one of the arguments, and the other is put in the place of the misplaced side of the operator.

  • (4^): the left side is the same to (^) 4, or more formal a -> 4 ^ a

  • (^4): the right side is the same to flip (^) 4, or more formal a -> a ^ 4

Sectioning a function is very useful because it allows you to write a function, without giving a particular name to that function; for example:

  • (1+): the function which increments by one a value

  • (2*): the function which doubles a value

  • (' ':): the “tab” function

The minus operator cannot make a right section, since it would be deciphered as unary invalidation in the Haskell language. The Prelude capacity “subtract” is accommodated this reason.

Local Declarations

In Haskell, local declarations can be used. This is done by using the let keyword, which binds a particular value to a variable; for example:

Prelude> let a = -5 + 1 in a + 10
6

This is equivalent to

{
 int a = -5 + 1;
 ... a + 10 ... ;
 }

In Haskell , the variable receives a value that cannot be changed.

Partial Application

When a function is called with some arguments missing, you actually obtain another function. This can be seen as something like “underloading,” but the specific term is partial application. For example, let’s think about the power function. It takes two arguments: namely, the base and the exponent. If you have a program in which only the irrational number e is used as a base, you could define a new function that takes one argument— namely, the exponent, as follows.

Prelude> powE x = (exp 1) ^ x
powE :: (Integral b, Floating a) => b -> a
Prelude> powE 5
148.41315910257657

This is very useful when you have complex functions with many parameters, but most of the time, you do not use all parameters.

You need to pay attention to an important aspect: the functions are not partial; you only partially apply a function.

Pattern Matching

In pattern matching , you need to specify patterns in which the data could fit, then check which patterns fit, and then manipulate the data as the pattern indicates. A function could have many implementations for various patterns, an approach that leads to organized, clean, and easy-to-read code. Pattern matching could be used with any data type. The following is an example of a function that tests if a number is zero (it was written in a file called Main.hs).

main = testInteger 5

testInteger 0 = print "zero"
testInteger x = print "not zero"

And in GHCi, after changing the work directory.

Prelude> :load Main
[1 of 1] Compiling Main             ( Main.hs, interpreted )
Ok, modules loaded: Main.
Prelude> testInteger 0
"zero"
Prelude> testInteger 6
"not zero"

When a function contains patterns, they are checked from top to bottom. When a match is found, the corresponding implementation is chosen. The “-5” argument fits in the second pattern, so a corresponding message is printed. You could use the if-else statement, but let’s think about how a function would look if it contains a branched if-else. Let’s say that you write a function that prints the name of a color according to a number as an argument, as shown in the following. If you did not use patterns, the branched if-else would become difficult to follow.

color 1 = print "green"
color 2 = print "blue"
color 3 = print "red"
color 4 = print "orange"
color x = print "any color"

Here are some arguments.

Prelude> color 1
"green"
Prelude> color 5
"any color"
Prelude> color (-100)
"any color"

You need to pay attention to the order of patterns. If you put the last pattern on the first place, then, always would be typed “any color”, because any number would fit in x.

If the function does not contain a general pattern, and you call it with an argument that does not fit in any pattern, then you will get an error message (after removing the last line of the function).

Prelude> color (-100)
*** Exception: Main.hs:(3,1)-(6,24): Non-exhaustive patterns in function color

You can use patterns in function that have tuples as parameters; for example, you have two points in the xOy axis system and you want to compute the distance between them. First, let’s remember the formula for the Euclidean distance: given two points, A(x 1, y 1), B(x 2, y 2), the distance AB is given by the formula $$ AB=sqrt{{left({x}_1-{x}_2
ight)}^2+{left({y}_1-{y}_2
ight)}^2} $$. You would do it like this:

distance x y = sqrt((fst x - fst y)^2 + (snd x - snd y)^2)

Or, if involving patterns, it would be

distance2 (x1,y1) (x2, y2) = sqrt((x1 - x2)^2 + (y1 - y2)^2)

In Haskell, there are already defined functions that return the first or the second component of a pair. But what if you would work with triples? You can write your own functions.

firstElement :: (a, b, c) -> a
firstElement (x, _, _) = x
secondElement :: (a, b, c) -> b
secondElement (_, y, _) = y
thirdElement :: (a, b, c) -> c
thirdElement (_, _, z) = z

The underscore ( _ ) means that you can put anything on that index.

Guards

You use guards when you want to test if a property for one or more values is true or false. Of course, you could use the if statement, which is similar to guards, but guards are easy to read and they fit well with patterns. The following is an example in which the name of the weekday is typed according to a number as parameter.

dayOfWeek day
 | day == 1 = print "Monday"
 | day == 2 = print "Tuesday"
 | day == 3 = print "Wednesday"
 | day == 4 = print "Thurday"
 | day == 5 = print "Friday"
 | day == 6 = print "Saturday"
 | day == 7 = print "Sunday"
 | otherwise = print "The week has only 7 days. Please, choose an integer between [1, 7]."

The guard is represented by the pipes symbol (|). In essence, it is a Boolean expression. The expressions are evaluated one at a time, in order of appearance. If the result is True, then the corresponding instructions are used; else, the next expression follows, until it finds the expression whose result is True. For example, if you call the preceding function with 3, first, it is evaluated as day == 1 (which is False); then, day == 2 (also False); then, day == 3 (which is True), so the corresponding instruction is used.

The guards are useful when there is a long-branched if-else statement. Imagine how the code would look if you used if-else. Usually, the end guard is otherwise, which covers all the possibilities that are not found explicitly in the previous guard. For example, what if you gave the number 8 as a parameter to our function? Of course, an eighth day of the week does not exist, so the result is "The week has only 7 days. Please choose an integer between [1, 7] . " What about 100 as an argument? Or 0? Or –5? Of course, they are included in the last guard. If all guards are evaluated as False (this is the case when you do not include otherwise), then you will get an error message (after removing the last line).

Prelude> dayOfWeek 10
*** Exception: Main.hs:(7,1)-(14,28): Non-exhaustive patterns in function dayOfWeek

The guard could be written on the same line, but it would be difficult to follow. Also, a function that uses a guard could have as many parameters as you want.

Instance Declarations

A declaration of an instance has the following form.

instance (asserion_1, ..., assertion_n) => class type_1 ... type_m where ...

An instance declaration has two parts.

  • context: the left side of =>

  • head: the right side of =>

When GHCi resolves a constraint, each instance declaration tries to be matched with the constraint through initialization of the head of instance declaration. Let’s say you have the following statements:

instance ctx1 => C Int a     where ...
instance ctx2 => C a   Bool  where ...

Implicitly, GHCi knows that exactly one instance should fit the constraint on which it tries to resolve.

Rules for the Head

There are rules for the head of an instance.

  • First rule. -XTypeSynonymInstances, which defines an instance of a type class for the type synonyms

  • Second rule. -XFlexibleInstances, which permits to define an instance of a type class with arbitrary encapsulated types from the head of the instance

The rules become less restrictive when used.

With - XTypeSynonymInstances , the heads of the instance could use type synonyms. The following code is correct.

Type Pt x = (x, x)
Instance C (Pt x) where ...

With -XFlexibleInstances , the head could mention arbitrary imbricated types. This flag implies the previous flag.

Rules for the Context

The -XFlexibleContexts moderates a rule according to the context of an instance declaration, which should have the C a format (a is a type variable that occurs in the head). More specifically, this flag permits constraints over a class that has the format (C t1 … tn) in the context of an instance declaration, but it does not interfere with the restrictions over equality.

The declaration of an instance should respect the rules for instance termination.

Rules for Instance Termination

-XUndecidableInstances defines instances that could result in type-checker non-termination.

These are the rules.

  • For every class restriction from the context

    1. A type variable should not have more appearances in the restriction than in the head.

    2. The constructors and the variable for the restrictions are fewer.

    3. The constraints do not mention type functions.

  • For any functional dependency of the class, each type variable from the right should appear in the left of the substitution mapping.

These conditions assure that the termination of the resolution occurs. In every level of reduction, the problem shrinks with at least one constructor.

Other Lists

In the previous section, you used only finite lists, but in Haskell, lists can be infinite because of the lazy evaluation property. The following list represents an infinite list of integer numbers, which starts with 10.

[10..]
Note

The evaluation could be interrupted with CTRL+C command.

If you want a list of the first 50 integers, you would not write all of them; you would do like this:

Prelude> [1..50]

This approach is called ranges, which enumerates the elements of a list (it has an enumeration rule!) in a compact manner.

Prelude> ['a'..'e']
"abcde"
Prelude> [5,10..50]
5,10,15,20,15,30,35,40,45,50

Still, the enumeration rule should be intuitive. You cannot write [1,3,9,27..50] to get all powers of 3 less than 50. In order for elements to be in decreasing order, it is not enough to write [50..1]; you should write [50,49..1].

Haskell allows ranges in floating-point numbers, but it is imprecise; often the results are not the expected ones.

In the next example, you write two ways to retrieve the first five multiples of 2.

Prelude> [2,4..5*2]
2,4,6,8,10
Prelude> take 5 [2,4..]

Three functions create infinite lists: cycle, repeat, and replicate.

Prelude> take 4 (cycle [1, 2])
[1,2,1,2]
Prelude> take 2 (cycle "Haskell")
"HaskellHaskell"
Prelude> take 5 (repeat 2)
2,2,2,2,2
Prelude> replicate 5 2
2,2,2,2,2

You know that in mathematics , the sets could be defined using predicates. A simple example is $$ A=left{3x|xin N,x<7
ight} $$. For this set, 3x is called the output; x represents the variable; N represents the positive numbers set; and x < 7 represents the predicate. A simple way to get this set is to write take 7 [0,3..], but you could also proceed as follows.

Prelude> [3*x | x <- [0..6]]
0,3,6,9,12,15,18

This representation of lists is known as list comprehension.

Now, let’s use more predicates. If you want numbers that are multiples of 5, in the range 100 to 150, you would proceed as follows.

Prelude> [x| x <- [100, 150], x `mod` 5 == 0]
100,105,110,115,120,125,130,135,140,145,150

Arrays

Haskell permits the use of an array, which has only one constructor, Array. As any data structure, they are immutable, so they cannot change the value. Still, it exists to “modify” an array, but this means that it created a new array with extra features, without altering the original array.

Immutable Arrays

The immutable arrays belong to the Data.Array.IArray module. The operations applied on them are the same as those applied on Array.

Mutable Arrays

Mutable arrays belong to the Data.Array.IO module. They permit operations through elements that are updated in-place. The create, update, and query functions for arrays that pertain to the IO monad.

import Data.Array.IO
main = do myArray <- newArray (1,5) 20 :: IO (IOArray Int Int)
           a <- readArray myArray 1
           writeArray myArray 1 25
           b <- readArray myArray 1
           print (a,b)

The preceding code builds an array with five elements, all of them having the value 20. Next, it reads the element from the first index and changes it, and then reads it again. In the last step, it prints the pair that contains the initial value and the new value.

Another mutable array belongs to the Data.Array.ST module, which permits the use of mutable array from the ST monad.

import Control.Monad.ST
import Data.Array.ST


 oldNewValue = do myArray <- newArray (1,5) 20 :: ST s (STArray s Int Int)
                a <- readArray myArray 1
                writeArray myArray 1 25
                b <- readArray myArray 1
                return (a,b)


 main = print $ runST oldNewValue

The preceding code essentially does the same thing as the previous example, but it uses a different approach.

In addition to these two types of arrays, there are other types that belong to the following modules: Data.Array.Diff, Data.Array.Unboxed, and Data.Array.Storable.

Finite Maps

Finite maps (or simple, maps) represent lookup tables for functional programming. In imperative programming, hash tables are equivalent to maps in Haskell. There are a couple of forms (key and value). When you work with maps, you need to import the Data.Map module.

import Data.Map
daysOfWeek = fromList
        [ ("M",  "Monday")
        , ("T",  "Tuesday")
        , ("W",  "Wednesday")
        , ("Th",  "Thursday")
        , ("F",  "Friday")
        , ("S",  "Saturday")
        , ("Su",  "Sunday") ]

A map could be converted to a list by using toList.

 Prelude> toList daysOfWeek
    [("F","Friday"),("M","Monday"),("S","Saturday")
    ,("Su","Sunday"),("Th","Thursday"),("T","Tuesday")
    ,("Wed","Wednesday")]

As you can see, the order is not kept.

If you need only the keys, then use the keys command; if you need only the elements, then use the elems command.

Prelude> keys daysOfWeek
    ["F","M","S","Su","Th","T","W"]
*Main> elems daysOfWeek
    ["Friday","Monday","Saturday","Sunday","Thursday","Tuesday","Wednesday"]

You could also search a value in the map by using the lookup function, as follows.

 Prelude> Data.Map.lookup "W" daysOfWeek
    "Wednesday"

You use the long version of the lookup function because there also exists a lookup function in Prelude, and you need to disambiguate so that GHCi knows what function to use.

Layout Principles and Rules

This section briefly talks about xmonad and wxHaskell.

xmonad

The following appears on the Haskell community’s official site ( http://xmonad.org/about.html ):

xmonad is a tiling window manager for the X Window system, implemented, configured, and dynamically extensible in Haskell. This demonstration presents the case that software dominated by side effects can be developed with the precision and efficiency you expect from Haskell by utilizing purely functional data structures, an expressive type system , extended static checking and property-based testing. In addition, you describe the use of Haskell as an application configuration and extension language.

As mentioned, xmonad is a dynamic window manager for Windows. Written in Haskell, it automatically presents windows and permits any number of workspaces and floating windows. Additionally, it supports many screens, each with its own workspace. Most elements can be customized.

xmonad can be installed in one of the following packages.

  • xmonad: the standard package

  • xmonda-contrib: uses different algorithms, settings, and so forth, from third parties

  • xmonad-git or xmonad-contrib-git: the package for developers (includes additional dependencies)

xmonad is very powerful. It has an application in cloud computing, too, because it can act as a window manager for big data engineering.

wxHaskell

wxHaskell is a compact and native graphical user interface (GUI) for Haskell (written in C++), on which developers create a GUI with a functional language. There are many applications developed using wxHaskell, including Dazzle, hsReversi, GeBoP, Proxima, Functional Forms, HCPN, and HpView.

The following is samples code that creates a frame and a menu in the frame.

fr <- frame [text := "Main Window", clientSize := sz 600 600]
pnl <- panel f []


mainMenu <- menuPane [text := "File"]
openItem <- menuItem timerMenu [text :="Open..."]
saveItem <- menuQuit timerMenu [help := “Save"]

The Final Word on Lists

Pattern matching is a very important aspect of lists.

Prelude> let xs = [(-1, 0), (54,-23), (2,1), (15,-8), (5,12), (0,1)]
xs :: (Num t1, Num t) => [(t, t1)]
Prelude> [x+y | (x,y) <- xs]
[-1,31,3,7,17,1]

If pattern matching breaks down, then it moves to the next component.

The list itself could be utilized in pattern matching. Something could be matched with the empty list [], or whatever pattern that includes []. The [1,2,3] is actually a representation for 1:2:3:[], because you first add 3 to the empty list, and then 2, and then 1, obtaining [1,2,3], so you have used the former pattern. This represents the pattern x:xs, which adds the x element as the head of the xs list. The x:xs pattern is widely utilized, especially in the recursive functions. Note that when you use the : symbob in patterns, it is applied to lists that have at least one element.

If you want to link, for example, the first two components to variables and the remaining elements to another variable, you can use x:y:ys. The matching will be applied only to lists with at least two components.

Let’s implement our own function that retrieves the head of a list.

headFunction :: [x] -> x
headFunction [] = error "The list in empty."
headFunction (a:_) = aChecking if it works:

And some arguments.

Prelude> headFunction [1,2,3,4]
1
Prelude> headFunction ['a', 'b']
'a'

Let’s look at our example. It uses the function error, which has a parameter and a string. When this error arises, the program crashes and the message is the string given as a parameter.

Now, let’s display the first two elements of a list.

elements :: (Show a) => [a] -> String
elements [] = "The list has no elements."
elements (a:[]) = "The only element of the list is " ++ show a
elements (a:b:[]) = "The only two elements of the list are " ++ show a ++ " and " ++ show b
elements (a:b:_) = "The list has more than 2 elements, but the first are " ++ show a ++ " and " ++ show b

The function covers all cases: an empty list and a list with one or more elements. Remember that a:[] is equivalent to [a], and a:b:[] is equivalent to [a,b], but you cannot write a:b:_ with squared braces.

Now let’s write our length function, using patterns and recursion.

lengthFunction [] = 0
lengthFunction (_:xs) = 1 + lengthFunction xs

Let’s test the function.

Prelude> lengthFunction []
0
Prelude> lengthFunction [1,2,3]
3
Prelude> lengthFunction "abc"
3

A special pattern is as pattern. It represents a way to split an element according to a pattern, and links it to names, but it is retained as a reference to the whole element. This is done using an @ in front of the pattern; for example, ys@(y:z:zs). This matches the same element that y:z:zs matches, but the entire list ys could be obtained.

Advanced Types

Until now, you have used many data types, such as Bool, Int, and Char, but Haskell allows us to define our data types . This is done by using the data keyword.

Prelude> data BoolValue = False | True
data BoolValue = False | True

Let’s say that you want to represent a triangle by giving the values of the sides: (15, 5.3, 12.7), but this triple could represent anything. But what if you would have a square? This is defined by the length of the side. So, you need a more general type, such as Figure.

Data Figure = Triangle Float Float Float | Square Float

The value constructor of the Triangle has three elements of type Float. Also, a Figure could be Square, defined only by a variable whose type is Float.

Prelude> data Figure = Triangle Float Float Float | Square Float
data Figure = Triangle Float Float Float | Square Float
Prelude> :t Triangle
Triangle :: Float -> Float -> Float -> Figure
Prelude> :t Square
Square :: Float -> Figure

Next, let’s compute the perimeters of the two figures.

perimeter :: Figure -> Float
perimeter (Triangle a b c) = a + b + c
perimeter (Square a) = 4 * a

Let’s observe that the type of the perimeter is Figure. You have chosen Figure because it is a type; unlike Triangle or Square. Now, let’s compute the perimeter for a triangle, and then for a square.

Prelude> perimeter $ Triangle 5 6 7
18.0
Prelude> perimeter $ Square 12.5
50.0

You tell Haskell which figure should have computed the perimeter by using the $ symbol. If you want that our data type to be derived from the Show type class, you proceed like this:

data Figure = Triangle Float Float Float | Square Float deriving (Show)

Now, you can write this in GHCi.

Prelude> Square 5
Square 5.0

You can use a previously defined data type to define a new data type . Let’s say you have a data type called Data, which takes three Ints for the year, the day, and the month. Now, you want to define a new data type called User, which takes two elements: an Int for the id and Data for the birthdate.

Prelude> data Data = Int Int Int deriving (Show)
data Data = Int Int Int
Prelude> data Person = Int Data
data Person = Int Data

Our defined data types could be exported into modules, including the functions.

module Figures
( Figure(..)
, perimeter
) where

Through Figure(..), you have put the value constructors for Figure in the module, so when this module is imported, it could use anything with Triangle and Square.

Monads

It is a little difficult to define a monad. In a few words, a monad provides a manner through which operations are chained together. In principle, all you do is write execution levels, and then link them together using a bind function (called >>=). The calls could be written to the bind operator, or it could be used as a syntax such that the compiler is forced to call that function. The purpose of a bind function is to pass the result of the current step to the next step. This is called the identity monad. But the other monads must do something in addition to be considered good . In fact, every monad implements the bind function in its own manner, and you could write a function that does a particular thing between execution steps. The following are some examples of monads.

  • Failure monad. If every step is marked as succeed/failed, you can bind to execute the following step just as if the last one succeed ; thus, if the result of a step failed, the entire sequence is automatically cancelled without any additional test.

  • Error or exception monad. Allow us to implement our own exceptions .

  • List monad. Every step has multiple results, and the bind function is applied over them, passing every result to the next step. This writes loops in every place that has multiple results.

Other useful monads are the Maybe monad, the Failure monad, the Reader/Writer monad, and the State monad.

A monad has three important components :

  • The type constructor m

  • The function return

  • The bind operator >=

The following shows how to use the Maybe monad .

return :: x -> Maybe x
    return a  = Just a


    (>>=)  :: Maybe x -> (x -> Maybe y) -> Maybe y
    m >>= g = case m of
                 Nothing -> Nothing
                 Just a  -> g a

Maybe represents the monad. The value is brought by the return through Just. If a value exists for m, then it applies g on it, and then the value is returned to the Maybe monad.

The monads fit very well with cloud computing.

Other Advanced Techniques

This section discusses higher order functions. In Haskell, a function could have another function as a parameter, or it could have another function as a result. These kinds of functions are called higher order functions. They are powerful when it comes to solving problems or issues with programs.

Every function in Haskell takes one parameter. How come? Let’s say that you want to obtain the minimum value between 2 and 3, by applying the min function. This function takes 2 as a parameter, and returns a value; then, the obtained value is compared (also applying min function) with 3. The following are the same.

Prelude> min 2 3
2
Prelude> (min 2) 3
2

If a function is called with too few parameters, then it becomes partially applied.

Now let’s write a function that has another function as a parameter. You write a function that returns the minimum value between 10 and another value.

Prelude> minTen x = min 10 x
Prelude> minTen 5
5

The infix functions could be partly applied when using sectioning. You do that using parenthesis.

Prelude> multiplyTwo = (*2)
Prelude> multiplyTwo 3
6

If you call multiplyTwo, then it is the same as 3*2, or (*2) 3.

In the following statement, you present a way in which a function is taken as a parameter and applied twice.

fTwo f x = f (f x)

The preceding example uses parenthesis because they are essential. First, f is applied on x, and then f is again applied, but the result is f x.

The following calls the fTwo function .

Prelude> fTwo (*2) 3
12

In the previous example, you see that functions of higher order are very useful in complex applications. Next, let’s write a function that adjoins to lists and then applies a function on all elements.

joinAndFunction _ [] _ = []
joinAndFunction  _ _ [] = []
joinAndFunction  f (a:as) (b:bs) = f a b : joinAndFunction  f as bs
Prelude> joinAndFunction (*) [1,2,3] [1,2,3]
[1, 4, 9]

In this manner, a single higher order function is used in many ways, bringing many benefits.

map, filter, takeWhile

The map function has a function and a list as parameters. It applies the function over each element of the list.

map _ [] = []
map f (x:xs) = f x : map f xs
Prelude> map snd [('a', 'b'), ('c', 'd')]
"bd"

Another useful function is filter. It has functions as parameters, which return a Boolean value (i.e., a predicate), and a list. It returns the elements that are satisfying the conditions of the predicate.

filter _ [] = []
filter p (x:xs)
| p x       = x : filter p xs
| otherwise = filter p xs

The map and filter functions could be replaced by list comprehension.

The takeWhile function has a predicate and a list as parameters. It returns the elements while the predicate stays True. It stops at the first element where the predicate becomes False.

Prelude> sum (takeWhile (<5000) [n^3 | n <- [1..], even (n^3)])

The preceding code computes the sum of even cubes smaller than 5000.

Lambdas

In essence, lambdas are unnamed functions, created to be used once. In general, they are used in functions of higher order, and they stay between parentheses. Also, you can use pattern matching inside of them. The distinction is that one parameter cannot have more than one pattern.

The following is an example.

Prelude> addNo a b c = a + b + c
Prelude> addNo 3 4 5
12

Summary

This chapter covered the main foundations for the user and programmer to understand the remainder of the book. It included the following topics.

  • A quick overview of the main advantages and disadvantages of object-oriented programming and functional programming

  • The basic elements used in Haskell and functional programming

  • Arithmetic operations

  • An introduction to pairs, triples, lists, monads, and more

  • Advanced techniques in higher order functions

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

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