© Alejandro Serrano Mena 2019
Alejandro Serrano MenaPractical Haskellhttps://doi.org/10.1007/978-1-4842-4480-7_1

1. Going Functional

Alejandro Serrano Mena1 
(1)
Utrecht, The Netherlands
 

Welcome to the world of Haskell! Before looking too deeply at the language itself, you will learn about what makes Haskell different from other languages and what benefits come with those differences. Haskell belongs to the family of functional languages, a broad set that includes ML, Lisp, Scala, and Clojure. If you have a background mostly in imperative or object-oriented languages, such as C, C++, or Java, you will see which of the ideas present in those languages can be transported into the functional world. If you already have experience with functional languages, you will see how other features in Haskell, such as lazy evaluation and type classes, make this language different from any other.

This book assumes some previous experience with the functional paradigm, regardless of the language, but not with Haskell. Also, some minimal practice with the shell or console is required.

After introducing Haskell, I will review how to install Haskell on your system. Finally, you will take your first steps with the language in the Glasgow Haskell Compiler (GHC) interpreter, a powerful tool that executes expressions in an interactive way. Throughout the book you will develop parts of a time machine web store; as with many things in life, the best way to learn Haskell is by writing Haskell programs!

Why Haskell?

If you are reading this book, it means you are interested in learning Haskell. But what makes this language special? Its approach to programming can be summarized as follows:
  • Haskell belongs to the family of functional languages.

  • It embodies in its core the concept of purity, separating the code with side effects from the rest of the application.

  • The evaluation model is based on laziness.

  • Types are statically checked by the compiler. Also, Haskell features a type system that is much stronger and expressive than usual.

  • Its approach to polymorphism is based on parametricity (similar to generics in Java and C#) and type classes.

In the rest of this section, you will understand what the terms in this list mean and their implications when using Haskell. Also, you will get a broad view of the entire Haskell ecosystem in a typical distribution: the compiler, the libraries, and the available tools.

Why Pure Functional Programming?

Functional programming is one of the styles, or paradigms, of programming. A programming paradigm is a set of concepts shared by different programming languages. For example, Pascal and C are part of the imperative paradigm, and Java and C++ mix the imperative paradigm with the object-oriented one. The fundamental emphasis of functional programming is the empowerment of functions as first-class citizens. This means functions can be manipulated like any other type of data in a program. A function can be passed as an argument to another function, returned as a result, or assigned to a variable. This ability to treat functions as data allows a higher level of abstraction and therefore more opportunities for reuse.

For example, consider the task of iterating through a data structure, performing some action on each element. In an object-oriented language, the implementer of the structure would have surely followed the iterator pattern, and you as a consumer would write code similar to the following Java code:
Iterator it = listOfThings.iterator();
while (it.hasNext()) {
    Element e = it.next();
    action(e); // perform the action
}
As you can see, there is a lot of boilerplate code in the example. In Haskell, you would use the map function , which takes as its argument the action to perform on each element. The corresponding code is as follows:
map action listOfThings

The code now is much more concise, and the actual intention of the programmer is explicit from the use of the map function. Furthermore, you prevent any possible issue related to applying the iterator pattern poorly because all the details have been abstracted in a function. Actually, a function such as map is common in functional code, which gives you confidence that any bug in its implementation will be found quickly.

Performing the same task in Java (up to version 7) requires, on the provider side, you to create an interface that contains the function that will perform the operation. Then, on the user side, you need to implement that interface through a class or use an anonymous class. This code will be much longer than the one-line version you saw earlier. In fact, new versions of Java (from version 8 on), C++, and C# (from the release of .NET Framework 3.5) are embracing functional concepts and will allow you to write code similar to the previous line.

In Haskell, a piece of code consists of expressions, which are evaluated in a similar fashion to mathematical expressions. In an imperative language, methods consist of statements that change a global state. This is an important distinction because in an imperative program the same piece of code may have different results depending on the initial state when it is executed. It’s important to notice here that elements outside of the program control (known as side effects), such as input and output, network communication, and randomness, are also part of this global state that may change between executions of the same function.

Expressions in Haskell cannot have side effects by default; these expressions are called pure. A common misunderstanding about functional programming is that it disallows any kind of change to the outer state. This is not true; side effects are possible in Haskell, but the language forces the programmer to separate the pure, side-effect-free parts from the “impure” ones.

The main benefits of purity are the improved ability to reason about the code and an easier approach for testing the code. You can be sure that the outcome of a pure function depends only on its parameters and that every run with the same inputs will give the same result. This property is called referential transparency, and it’s the foundation for applying formal verification techniques, as you will see in Chapter 15.

Pure functions are easier to compose because no interference comes to life in their execution. Actually, the evaluation of pure expressions is not dependent on the order in which it is done, so it opens the door to different execution strategies for the same piece of code. This is taken advantage of by the Haskell libraries providing parallel and concurrent execution and has even been used for scheduling code in a GPU in the Accelerate library.

By default, Haskell uses an execution strategy called lazy evaluation. Under laziness, an expression is never evaluated until it is needed for the evaluation of a larger one. Once it has been evaluated, the result is saved for further computation, or it’s discarded if it’s not needed in any other running code. This has an obvious benefit because only the minimal amount of computation is performed during program execution, but it also has drawbacks because all the suspended expressions that have not yet been evaluated must be saved in memory. Lazy evaluation is powerful but can become tricky, as you will see in Chapter 5.

Why Strong Static Typing?

Type systems come in various formats in almost all programming languages. A type system is an abstraction that categorizes the values that could appear during execution, tagging them with a so-called type. These types are normally used to restrict the possible set of actions that could be applied to a value. For example, it may allow concatenating two strings but forbid using the division operator between them.

This tagging can be checked, broadly speaking, at two times: at execution time (dynamic typing), which usually comes in languages with looser typing and allows implicit conversions between things such as integers and strings, or at the moment of compilation (static typing), in which case programs must be validated to be completely well typed in terms of the language rules before generating the target output code (usually machine code or bytecode) and being allowed to run. Haskell falls into this second category: all your programs will be type checked before they are executed. Within statically typed languages, some of them, such as Java or C#, need to perform extra type checking at runtime. In contrast, once a Haskell program has been compiled, no more type checks have to be done, so performance is vastly increased.

Haskell’s type system is very strong. Strength here means the number of invariants that can be caught at compile time before an error materializes while the application is running. This increases the confidence in code that is type checked, and it’s common to hear the following in Haskell circles: “Once it compiles, it works.” This strong typing gives rise to a way of programming dubbed type-oriented programming. Basically, programmers know the type of the function they are developing and have a broad idea of the structure of the code. Then, they “fill in the holes” with expressions from the surrounding environment that fit into it. This approach has actually been formalized, and there is another language similar to Haskell, called Agda, which comes with an interactive programming environment that helps in filling in the holes and even does so automatically if only one option is available at one place.

In Chapters 13 and 15, I will move a bit from Haskell to Idris, a language with a similar syntax that features dependent typing. Dependent typing is an even stronger form of type checking, where you can actually express invariants such as “If I concatenate a list of n elements to a list with m elements, I get back a list with n+m elements” or “I cannot get the first element of an empty list.” Then, you will see how some of these techniques can be transferred as patterns into Haskell.

The last difference in Haskell with respect to typing comes from polymorphism. The problem is twofold. First, you want to write functions on lists without caring about the type of the elements contained in them. This is known as parametric polymorphism, and you will explore it in Chapter 3. In other cases, you want to express the fact that some types allow some specific operations on their values. For example, the idea of applying a function to all elements in a list, as you did before with map, can be generalized into the concept of having an operation that applies a function to all elements in some data structure, such as a tree or a graph. The solution here is called type classes , which groups different types with a common interface. You will look at it in Chapter 4, where you will also realize that this concept is a very high-level one that allows for expressing several abstract ideas (functors, monads) and that gives an interesting flavor to Haskell code.

The Haskell Ecosystem

Until now I have spoken only about Haskell the language. But the benefits of Haskell come not only from the language but also from the large and growing set of tools and libraries that can be used with the language.

Several compilers for Haskell are available, which usually take the name of a city: GHC (from Glasgow), UHC (from Utrecht), and so on. Of those, GHC is usually taken as the standard, and it’s the one with the largest number of features. At the moment of writing, only GHC is actively maintained. You will follow this path and will work with GHC throughout the book.

Like any other popular programming language, Haskell has an online repository of libraries. It is called Hackage, and it’s available at http://hackage.haskell.org/ . A stable subset of Hackage, known as Stackage, is available at https://www.stackage.org/ . Both repositories integrate seamlessly with Cabal and Stack, the two alternative building tools for Haskell projects. In Hackage you can find libraries ranging from bioinformatics to game programming, window managers, and much more.

Apart from GHC and Cabal, in the book you will look at some tools that aim to help developers write better code faster. The first one will be the GHC profiler; you will learn about it in Chapter 5 to detect space and time leaks. You will also look at Hoogle and Haddock, which are used to browse and create documentation. In Chapter 14, you will use the UU Attribute Grammar System to help you build domain-specific languages.

The History of Haskell

Haskell is usually considered the successor of the Miranda programming language, which was one of the most important lazy functional programming languages in the 1980s. However, at that time, lots of other languages of the same kind existed in the wild. That made it difficult for researchers to have a common base in which to perform experiments in this paradigm. So, by the end of that decade, they decided to build a completely new language that would become the groundwork for that research.

During the 1990s, several versions of the language were produced. During this time Haskell evolved and incorporated some of the features that give the language its particular taste, such as type classes and monads for managing input and output. In 1998, a report defined Haskell 98, which was taken as the standard for any compliant Haskell compiler. This is the version targeted by most library developers.

However, new ideas from researchers and compiler writers were integrated into Haskell compilers, mostly in GHC. Some of these extensions became widely used, which made the case for a revised Haskell standard, which came out in 2010. At the time of this writing, GHC targets this version of the language.

As the language has become more popular, more extensions have been added to GHC and other compilers, and these features usually can be switched on or off at the developer’s will. As a result, a more disciplined schedule has been created for issuing revised Haskell standards on a timely basis.

Your Working Environment

At this point you are probably feeling the need to try Haskell on your own computer. The first step for this is, of course, to have a working Haskell installation on your system. Haskell developers worried in the past about how to get people ready fast and easily. So, they created the Haskell Platform, a distribution containing the GHC compiler, the Cabal build and library system, and a comprehensive set of libraries. To get the Haskell Platform, go to http://www.haskell.org/platform/ . Then, follow the steps corresponding to the operating system you will be using.

Installing on Windows or Mac OS X

Installing on the Microsoft or Apple operating system is easy because the file you download is an executable package that will take care of everything.

Installing on Linux

The world of Linux distributions is diverse, so it’s difficult to suggest the best way to get a working Haskell installation on Linux systems. If you use a distribution supporting some sort of package management system, it’s better to stick with that system. For example, Debian-based systems support apt-get. Thus, you can run the following:
$ sudo apt-get install haskell-platform
The best known of Debian derivative, Ubuntu, features a different way to get the GHC compiler and the Cabal build tool up and running. Herbert V. Riedel, one of the maintainers of the Platform, provides a specific repository for this system, which you can get by running
$ sudo add-apt-repository ppa:hvr/ghc
$ sudo apt-get update

Tip

If the call to add-apt-repository does not work, ensure that you have the corresponding package installed. You can get it using sudo apt-get install software-properties-common.

This repository gives access to every version of GHC since 7.0. To install the latest GHC and Cabal at the moment of writing, you need to do the following:
$ sudo apt-get install ghc-8.6.3 cabal-install-2.4

In addition, you should also add the folder /opt/ghc/<version>/bin to your PATH. How to do so depends on the shell you are using, but in the default configuration adding a line to .bashrc should be enough.

In Fedora and other Red Hat–based distros, this is the line to run:
$ yum install haskell-platform

You can also check the whole list of distributions that have Haskell Platform out of the box on the Haskell Platform website.

Installing on Linux from Source

In case you want or need to perform a complete installation from source code, you must follow these steps:
  1. 1.

    Go to the GHC compiler web page, http://www.haskell.org/ghc/ . Click the Download link and get the binary package for the latest stable release.

     
  2. 2.

    Uncompress the file you just downloaded into the place you want it to live. It’s recommended that you add that folder to your PATH. You may need to install some libraries, like GMP, to be able to run this binary. In Debian and derivatives, those dependencies may be obtained by running sudo apt-get build-dep ghc.

    Note You can also build GHC from source. However, this is a tedious and error-prone process, so using just the binary distribution is recommended. In case you want to follow that path, the Haskell wiki page has a detailed description of the process; see http://ghc.haskell.org/trac/ghc/wiki/Building .

     
  3. 3.

    Return to the Haskell Platform page to download its source.

     
  4. 4.

    Uncompress, build, and install it, which is usually accomplished by running this:

     
$ tar -xzvf haskell-platform-*.tar.gz
$ cd haskell-platform-*
$ ./configure
$ make
$ make install

First Steps with GHCi

It’s now time to see whether your Haskell Platform is correctly installed. To do so, open a console, type ghci –e 5+3, and press Enter. You should see 8 as output. This application is one instance of a read-eval-print loop (REPL), or, more succinctly, an interpreter. In GHCi, you input an expression and press Enter. The expression gets evaluated, and the result is shown on the screen. This allows for a programming methodology where you navigate into and discover the functionality of a library by issuing commands in the interpreter and also test your code interactively.

To open an interpreter in a console, just run ghci. A prompt with Prelude> at the beginning should appear. This line tells you that the interpreter is ready to accept commands and that the only loaded module at this moment is the Prelude, which contains the most basic functions and data types in Haskell. As a first approximation, GHCi can work as a fancy calculator, as shown here:
Prelude> 5 * 3
15
Prelude> 1/2 + 1/3
0.8333333333333333
If you now type s and press the Tab key, you will see a list of all possible functions beginning with that letter. If you then type q and press Tab again, only one possibility is left, sqrt, which is automatically written for you. One distinguishing choice made by Haskell creators was that parentheses are not used when applying a function. This means that if you want to find the square root of 7, you just write this:
Prelude> sqrt 7
2.6457513110645907

There are many other arithmetic operations you can perform in the interpreter: sin, cos, log, exp, and so forth. In the next chapter you will learn how to use strings and lists and how to define functions, which will make your experience with the interpreter much more rewarding.

GHCi does not by default allow you to input several lines of code. For example, if you want to break the previous addition of two rational numbers into two lines, you cannot do it easily. Try entering the expression again, but press Enter after inputting the plus sign. If you press Enter, this error message will be produced:
Prelude> 1/2 +
<interactive>:2:6:
    parse error (possibly incorrect indentation or mismatched brackets)
The solution is to start a multiline block. A multiline block is an expression that is allowed to span more than one line. To do so, enter :{ and then press Enter. The prompt will change into Prelude|, showing that the input is expected to fill several lines. To end the block, enter the opposite of the beginning symbol, :}. Here’s an example:
Prelude> :{
Prelude| 1/2 +
Prelude| 1/3
Prelude| :}
0.8333333333333333

Caution

To start a multiline block, :{ must be the only text entered in the first line.

All the internal actions of the interpreter (i.e., those that are not functions on any library) start with a colon. For example, typing :? and pressing Enter lists all the available commands. Other possibilities are looking at the language standard version you are using, in this case Haskell 2010 with some customizations. Here’s an example:
Prelude> :show language
base language is: Haskell2010
with the following modifiers:
  -XNoDatatypeContexts
  -XNondecreasingIndentation
I stated before that Haskell has a strong static type system. You can check that it forbids dividing two strings (which are written between double quotes), producing an error when input in the interpreter, like so:
Prelude> "hello" / "world"
<interactive>:2:9:
    No instance for (Fractional [Char]) arising from a use of `/'
    Possible fix: add an instance declaration for (Fractional [Char])
    In the expression: "hello" / "world"
    In an equation for `it': it = "hello" / "world"

Fractional is the name of the type class that provides support for the / operator. The error message is saying that in order to be able to divide two strings, you should tell the compiler how to do so, by adding a declaration with the code for the Fractional type class in the case of strings.

To close the interpreter and go back to the console, you can issue the command :quit or just press the key combination Ctrl+D. In both cases the result is the same.
Prelude> :quit
Leaving GHCi.

Note

GHCi is a powerful and customizable tool. You can find lots of tips and tricks on the Haskell wiki page devoted to the interpreter, https://wiki.haskell.org/GHC/GHCi .

The Time Machine Store

If you have already taken a look at the table of contents of this book, you will have noticed that it is divided into four parts. Each part is devoted to a different module of a small web store.
  • In this first part, you will learn how to define the basic blocks of your application, representing clients, products, and orders, and how to manage them in-memory.

  • In Part 2, you will develop some data-mining algorithms to get a better knowledge of the clients. In particular, you will develop a classification algorithm based on K-means and a recommendation algorithm.

  • Part 3 will deal with saving data into a persistent store. For product data you will use a custom file format, and for clients and orders you will use a more traditional database solution. With all of this, you will be able to build the initial application by Chapter 12.

  • Finally, in Part 4 you will see how a domain-specific language can be used to model special offers in the system, such as “20 percent discount for all clients in Europe younger than 30.”

What will you sell in this store? Time machines!

Welcome to the exciting world of time machines! These machines are quite special, and our clients come from all parts of the universe to get one. We would like to have a web store to handle all the orders. And we would also like to be developed in a language as special as our machines, like Haskell.

Sound exciting? Throughout this book you’ll be using Haskell to build your very own store for selling time machines. It’s a fun example, and it should keep the book interesting.

Summary

In this chapter you got familiar with Haskell.
  • You learned about the distinguishing features of pure functional programming and how it helps to build code that is more concise, more maintainable, and less error prone.

  • You looked at the benefits of having a strong, statically checked type system, like the one embodied in Haskell, and how dependent typing makes it possible to express invariants in a powerful way.

  • The major tools in the Haskell ecosystem were introduced: the GHC compiler, the Cabal build tool, the Hackage library repository, and the GHC interpreter. You also took your first steps with the interpreter.

  • You looked at the installation process of the Haskell Platform in the most common computer environments.

  • You were presented with the main target in the book (apart from learning Haskell): building a web store focused on selling time machines, with modules for simple data mining and offer descriptions.

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

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