We all love programs! On one side, there are surgical programming languages such as C and C++, which can solve the problem with clinical efficiency. This can be good and bad at the same time. A very experienced programmer can write a very efficient code; at the same time, it is also possible to write code that is unintelligible and very difficult to understand. On the other side, there are programs that are elegant, composable, and not only easier to understand, but also easier to reason with, such as Lisp, ML, and Haskell.
It is the second kind of programs that we will be looking at in this book. Not that efficiency is not important to us, nor does it mean that we cannot write elegant programs in C/C++. However, we will concentrate more on expressiveness, modularity, and composability of the programs. We will be interested more on the what of the program and not really on the how of the program.
Understanding the difference between what and how is very critical to understand the expressiveness, composability, and reasoning power of functional languages. Working with functional languages involves working with expressions and evaluations of expressions. The programmer builds functions consisting of expressions and composes them together to solve a problem at hand. Essentially, a functional programmer is working towards construction of a function to solve the problem that they are working on.
We will look at aÂ program written in Haskell. The program adds two integers and returns the result of addition as follows:
add :: Int > Int > Int add a b = a + b
Here, theÂ add
functionÂ takes two arguments, which are applied to the expression on the righthand side. Hence, the expression add a b
is equivalent to a + b
. Unlike programming languages such as C/C++, add a b
is not an instruction, but expressions and application of the values a
and b
to the expression on the righthand side and the value of the expression. In short, one can say thatÂ add a b
is bound to value of the expressiona + b
. When we callÂ add 10 20
, the expression is applied to the valuesÂ 10
and 20
, respectively. In this way, theÂ add
functionÂ is equivalent to a value that evaluates to an expression to which two values can be applied.
The functional program is free to evaluate the expression in multiple ways. One possible execution in a functional context is shown in the following diagram. You can see thatÂ add a b is an expression with two variablesÂ aÂ andÂ b as follows:
When value 10 is bound to variable b, the expression substitutes the value of b in the expression on the righthand side:
Now, the whole expression is reduced to an expression in a:
When value 20 is bound to variableÂ a, the expression again substitutes the value ofÂ aÂ in the expression on the righthand side:
Finally, the expression is reduced to a simple expression:
Note that in the expression add a b
, a
and b
Â can both be expressions. We can either evaluate the expressions before substitution, orÂ we can substitute the expressions first and then reduce them. For example, an expressionÂ add (add 10 20) 30
Â can be substituted in the expression a + b
Â as follows:
add (add 10 20) 30 = (10 + 20) + 30
Alternatively, it can be substituted by evaluating add 10 20
first and then substituting in the expression as follows:
add (add 10 20) 30 = add (10 + 20) 30 = add 30 30 = 30 + 30
The first approach is called call by name, and the second approach is calledÂ call by value. Whichever approach we take, the value of the expression remains the same. In practice, languages such as Haskell take an intelligent approach, which is more geared towards efficiency. In Haskell, expressions are typically reduced to weakheaded normal form in which not the whole expression is evaluated, but rather a selective reduction is carried out and then is substituted in the expression.
In this recipe, we will work withÂ Glasgow Haskell Compiler (GHC)Â and its interpreter GHCi. Then, we will write our first Haskell program and run it in the interpreter.
Note
We will use GHC throughout the book. GHC (https://www.haskell.org/ghc/)Â is the most widely used Haskell compiler. GHC supports Haskell 2010 language specifications (https://wiki.haskell.org/Definition). The current book is based on Haskell 2010 specifications. GHC supports an extensive range of language extensions.
WeÂ will install Stack, a modern tool to maintain different versions of GHC and to work with different packages. Perform the following steps:
 Install Stack. VisitÂ https://docs.haskellstack.org/en/stable/README/Â and follow the instructions for your operating system.
 Check that Stack works for yourÂ system by running the commandÂ
stack version
Â at the command prompt.
Check the latest GHC version by visiting https://www.haskell.org/ghc/. Set up GHC on your box by providing the GHC version number:
If you have already set up GHC on your box, then you will see theÂ following output:
 Pick up your editor.Â You can set up your favorite editor to edit Haskell code. Preferable editors are Emacs, Vi, and Sublime. Once you have picked up your favorite editor, ensure that the executables for the editor remain in your path or note down the full path to the executable.
Let's create a new project,Â hello
.Â Create the new projectÂ hello
Â by running the following command in the command prompt in an empty directory:
Note
Stack will select the latest package resolver. Stack will create a folder with the project name.
Change to project directory (
hello
) and runstack setup
. When run from the new project directory, Stack automatically downloads the corresponding GHC and sets it up. Compile andÂ build the project:
 You can now run the project using the following command:
 You should see the reply
someFunc
printed on the console. It means that the program compilation and execution was successful.
 Inspect theÂ
hello
Â project by opening an explorer (or file finder) and exploring thehello
directory: The project contains two main directories,
app
andsrc
. The library code goes into theÂsrc
folder, whereas theÂ main executable producing code goes into theÂapp
folder.  We are interested in theÂ
app/Main.hs
file.
 The project contains two main directories,
 Now, we will set an editor. You can set the editor by defining environment variableÂ
EDITOR
to point to the full path of the editor's executable.  Run the GHC interpreter by opening the command prompt and traversing to the
hello
project directory. Then, execute the commandstack ghci
. You will see the following output:
Set an editor if you haven't done so already. We are using Vi editor:
*Main Lib> :set editor gvim
 Open theÂ
Main.hs
Â file in the editor:*Main Lib> :edit app/Main.hs
This will open the
app/Main.hs
fileÂ in the window:
 Enter the following source in the editor:
module Main where  Single line comment! main :: IO () main = putStrLn "Hello World!"
 Save the source file and exit. You will see that GHCi has successfully loaded the saved file:
[2 of 2] Compiling Main ( d:\projects\hello\app\Main.hs, interpreted ) Ok, modules loaded: Lib, Main. *Main>
 Now, you can run theÂ
main
function that we have defined in the source file, and you will see theHello World
Â message:
*Main> main Hello World!
Exit the GHCi by running :quit
in the prompt.
 You can now rebuild and run the program by running the following commands:
stack build stack exec  helloexe
You will again see the outputÂ Hello World
Â as shown in the following screenshot:
This recipe demonstrated the usage of Stack to create a new project, build it, set up the corresponding GHC version, build the project, and run it. The recipe also demonstrated the use of the Haskell command prompt, aka GHCi, to load and edit the file. GHCi also allows us to run the program in the command prompt.
The recipe also shows the familiar Hello World!
program and how to write it. The program can be interpreted in the following way.
We will now look at different parts of the Main.hs
programÂ that we just created to understand the structure of a typical Haskell program. For convenience, the screenshot of the program is attached here:
The first line means that we are defining a module called Main
. The source that follows where is contained in this module. In the absence of any specifications, all the functions defined in the module are exported, that is, they will be available to be used by Â importing the Main
Â module.
The line number 3
(in the screenshot) that starts with 
is a comment. 
is used to represent a singleline comment. It can appear anywhere in the source code and comments on everything until the end of the line.
The next line is this:
main :: IO ()
This is a declaration of a function. :: is a keyword in Haskell, and you can read :: as has type. IO is a higher order data type as it takes a parameter (IO
is a special data type called IO monad; we will see more of it at the later).Â ()
is an empty tuple and is a parameter to IO
. An empty tuple in Haskell is equivalent to Unit Type. One can say that it is equivalent to void in imperative languages.
Hence,Â main :: IO ()
should be interpreted as follows:
main has a type IO ()
The next line actually defines the function:
main = putStrLn "Hello World"
It simply means that main
is a function whose value is equivalent to an expression on the righthand side,Â putStrLn "Hello World".
The putStrLn
is a function defined in Prelude
, and you can look up the type of the function by entering the following command in the prompt:
Prelude> :type putStrLn putStrLn :: String > IO ()
Here,Â putStrLn
has a type String > IO ()
. It means that putStrLn
is a function that, when applied and when the argument is of String
Â type, will have the resultant type IO ()
. Note how it matches with our type declaration of theÂ main
function.
The function declaration in the source code in Haskell is not compulsory, and the Haskell compiler can figure out the type of the function all by itself by looking at the definition of the function. You can try this by again editing the source file and removing declaration.
To edit the same file again, you can just issue the :edit
commandÂ without any parameter. GHCi will open the editor with the previously opened file. To reload the file again, you can issue theÂ :reload
Â command and GHCi will load the file.
Now, you can verify the type of main
functionÂ by issuing :t main
(:t
is equivalent to :type
). Verify that the type of main
is IO ()
.
If you visit the Stack website at https://www.stackage.org/, you will notice that Stack publishes nightly packages and Long Term Support (LTS) packages. While creating a new project, Stack downloads the latest LTS package list. It is also possible to provide the name of the LTS package explicitly by providing stack new â€“resolver lts9.2
.
In the project directory, you will notice two files:
<project>.yaml
<project>.cabal
The YAML file is created by Stack to specify various things, including LTS version, external packages, and so on. The .cabal
file is the main project file for the Haskell package. The cabal is the tool that Stack uses internally to build, package, and so on. However, there are several advantages of using Stack as Stack also supports prebuilt packages and manages cabal well. Furthermore, Stack also supports the Docker environment.
In this recipe, we will work with basic data types in Haskell. We will also define our own data types.
 Create a new project
datatypes
using Stack new data types. Change into the directorydatatypes
Â and build the new project usingstack build
.
In the command prompt, run stack ghci
. You will see the prompt. Enter this =:type (5 :: Int) =:
command:
*Main Lib> :type (5 :: Int) (5 :: Int) :: Int
:type
is a GHCi command to show the type of the expression. In this case, the expression is 5
. It means that the expression (5 :: Int
) isÂ Int
. Now, enter this :type 5
Â command:
*Main Lib> :type 5
5 :: Num t => t
 GHCi will interpret
5
as5 :: Num t => t
, which means that Haskell identified5
as some numericaltype t
.Num t => t
shows that the type ist
Â and that it has an extra qualification,Num
.Num t
denotes thatÂt
is an instance of a type classÂNum
.Â We will see type classes later. TheNum
class implements functions required for numericalÂ calculation. Note that the result of:type 5
is different fromÂ:type (5::Int)
.  Now, enter
:type (5 :: Double)
. You will see(5 :: Double) :: Double
. Do the same thing with5::Float
:
*Main Lib> :type (5 :: Double) (5 :: Double) :: Double
Note the difference between 5
, 5::Int
, 5::Float
, and 5::Double
. Without a qualification type (such as :: Int
), Haskell interprets the type as a generic type Num t => t
, that is,Â 5
is some type t
, which is a Num t
or numerical type.
Now enter following boolean types at the prompt:
*Main Lib> :type True True :: Bool *Main Lib> :type False False :: Bool
True
Â and False
are valid boolean values, and their type is Bool
. In fact, True
and False
are the only valid Bool
values in Haskell. If you try 1 :: Bool
, you will see an error:
*Main Lib> 1 :: Bool <interactive>:9:1: error: * No instance for (Num Bool) arising from the literal â€˜1â€™ * In the expression: 1 :: Bool In an equation for â€˜itâ€™: it = 1 :: Bool
Haskell will complain that 1
is a numerical type and Bool
is not a numerical type, which would somehow represent it (value 1
).
 Now, type
:type 'C'
in the prompt. GHCi will report its type to be'C' :: Char
.Char
is another data type and represents a Unicode character. A character is entered within single quotes.
 Get more information about each type. To do this, you can enter
:info <type>
in the prompt:
*Main Lib> :info Bool data Bool = False  True  Defined in â€˜ghcprim0.5.0.0:GHC.Typesâ€™ instance Bounded Bool  Defined in â€˜GHC.Enumâ€™ instance Enum Bool  Defined in â€˜GHC.Enumâ€™ instance Eq Bool  Defined in â€˜ghcprim0.5.0.0:GHC.Classesâ€™ instance Ord Bool  Defined in â€˜ghcprim0.5.0.0:GHC.Classesâ€™ instance Read Bool  Defined in â€˜GHC.Readâ€™ instance Show Bool  Defined in â€˜GHC.Showâ€™
This will show more information about the type. For Bool
, Haskell shows that it has two values False  True
Â and that it is defined in ghcprim0.5.0.0:GHC.Types
. Here,Â ghcprim
is the package name, which is followed by its version 0.5.0.0
and then Haskell tells that GHC.Types
is the module in which it is defined.
We have seen four basic types Int
, Double
, Char
, and Float
. More information about these types is given in the following table:
Type  Description  Remarks 
 Fixed precision integer type  Range [

 Single precision (  
 Double precision (  
 Character  
 Boolean values 

In previous sections of this recipe, when we ran the commandÂ :info Bool
, at GHCi prompt, Haskell also showed various instances of information. It shows more about the behavior of the type. For example,Â instance Eq Bool
means that the type Bool
is an instance of some type class Eq
. In Haskell, type class should be read as a type that is associated with some behavior (or function). Here, the Eq
type class is used in Haskell for showing equality.
In this recipe, we will work with pure functions and define simple userdefined data types. We will represent a quadratic equation and its solution using userdefined data types. We will then define pure functions to find a solution to the quadratic equation.
 Create a new project
quadratic
using the following command:
stack new quadratic
Change into the project folder.
 Delete
src/Lib.hs
Â and create a new filesrc/Quadratic.hs
to represent the quadratic equation and its solution.
 Open theÂ
quadratic.cabal
file, and in the section library, replaceLib
byQuadratic
in the tagexposedmodules
:
library hssourcedirs: src exposedmodules: Quadratic builddepends: base >= 4.7 && < 5 defaultlanguage: Haskell2010
 Open
Quadratic.hs
Â and add a module definition to it:
module Quadratic where
 Import the standard module
Data.Complex
to help us represent a complex solution to the quadratic equation.  Define the data type to represent the quadratic equation:
data Quadratic = Quadratic { a :: Double, b :: Double, c :: Double } deriving Show
This represents the quadratic equation of the form aâˆ—x2+bâˆ—x+c=0aâˆ—x2+bâˆ—x+c=0
. Â a
, b
, and c
represent the corresponding constants in the equation.
 Define the data type for representing the root:
type RootT = Complex Double
This represents that the complex data type parameterized by Double
. RootT
is synonymous to type Complex Double
(similar to typedef
in C/C++).
 A quadratic equation has two roots, and hence we can represent both the roots as follows:
import Data.Complex type RootT = Complex Double data Roots = Roots RootT RootT deriving Show
 Implement the solution. We will take a topdown approach to create a solution. We will define a toplevel function where we will implement a function assuming lower level details:
roots :: Quadratic > Roots
This shows that the roots
Â function takes one argument of type Quadratic
, and returns Roots
.
 Implement three cases mentioned next:
  Calculates roots of a polynomial and return set of roots roots :: Quadratic > Roots  Trivial, all constants are zero, error roots are not defined roots (Quadratic 0 0 _) = error "Not a quadratic polynomial"  Is a polynomial of degree 1, x = c / b roots (Quadratic 0.0 b c) = let root = ( (c) / b :+ 0) in Roots root root  b^2  4ac = 0 roots (Quadratic a b c) = let discriminant = b * b  4 * a * c in rootsInternal (Quadratic a b c) discriminant
We have referred to theÂ rootsInternal
Â function, which should handle case A, B, and C for the case III.
 Implement theÂ
rootsInternal
function to find all roots of the quadratic equation:
rootsInternal :: Quadratic > Double > Roots  Discriminant is zero, roots are real rootsInternal q d  d == 0 = let r = ((b q) / 2.0 / (a q)) root = r :+ 0 in Roots root root  Discriminant is negative, roots are complex rootsInternal q d  d < 0 = Roots (realpart :+ complexpart) (realpart :+ (complexpart)) where plusd = d twoa = 2.0 * (a q) complexpart = (sqrt plusd) / twoa realpart =  (b q) / twoa  discriminant is positive, all roots are real rootsInternal q d = Roots (root1 :+ 0) (root2 :+ 0) where plusd = d twoa = 2.0 * (a q) dpart = (sqrt plusd) / twoa prefix =  (b q) / twoa root1 = prefix + dpart root2 = prefix  dpart
Open src/Main.hs
. We will use the Quadratic
moduleÂ here to solve a couple of quadratic equations. Add the following lines of code in Main.hs
:
module Main where import Quadratic import Data.Complex main :: IO () main = do putStrLn $ show $ roots (Quadratic 0 1 2) putStrLn $ show $ roots (Quadratic 1 3 4) putStrLn $ show $ roots (Quadratic 1 3 4) putStrLn $ show $ roots (Quadratic 1 4 4) putStrLn $ show $ roots (Quadratic 1 0 4)
 Execute the application by building the project using
stack build
and then executing withstack exec â€“ quadraticexe
in the command prompt. You will see the following output:
Roots ((2.0) :+ 0.0) ((2.0) :+ 0.0) Roots ((1.5) :+ 1.3228756555322954) ((1.5) :+ (1.3228756555322954)) Roots ((1.5) :+ 1.3228756555322954) ((1.5) :+ (1.3228756555322954)) Roots ((2.0) :+ 0.0) ((2.0) :+ 0.0) Roots ((0.0) :+ 2.0) ((0.0) :+ (2.0))
A quadratic equation is represented by ax^2 + bx + c = 0
. There are three possible cases that we have to handle:
Case  Condition  RootÂ 1  Root 2  Remarks 
I 
 ERROR  ERROR  
II 

 Not applicable  Linear equation 
III 
 
IIIA 


 Perfect square 
IIIB 


 Real roots 
IIIC 


 Complex roots 
Â
We will define a module at the top of the file with the Quadratic
moduleÂ where the name of the module matches file name, and it starts with a capital letter. TheÂ Quadratic
moduleÂ is followed by the definition of module (data types and functions therein). This exports all data types and functions to be used by importing the module.
We will import the standard Data.Complex
module. The modules can be nested. Many useful and important modules are defined in the base package. Every module automatically includes a predefined module called Prelude. The Prelude exports many standard modules and useful functions. For more information on base modules, refer to https://hackage.haskell.org/package/base.
The userdefined data is defined by the keyword data followed by the name of the data type. The data type name always start with a capital letter (for example, data Quadratic
).
Here, we will define Quadratic
as follows:
data Quadratic = Quadratic { a :: Double, b :: Double, c :: Double } deriving Show
There are several things to notice here:
 The name on the lefthand side,Â
Quadratic
, is called type constructor. It can take one or more data types. In this case, we have none.  The name
Quadratic
on the righthand is calledÂ data constructor. It is used to create the value of the type defined on the lefthand side. {a :: Double, b :: Double, c :: Double }
Â is called theÂ record syntax for defining fields.a
,b
andc
are fields, each of typeDouble
. Each field is a function in itself that takes data type as the first argument and returns the value of the field. In the preceding case,
a
will have the function typeQuadratic > Double
, which meansÂa
will take the value of typeQuadratic
as the first argument and return the field a of typeDouble
.  The definition of data type is followed by deriving
Show
.Show
is a standard type class in Haskell and is used to convert the value toÂString
. In this case, Haskell can automatically generate the definition ofShow
. However, it is also possible to write our own definition. Usually, the definition generated by Haskell is sufficient.
We will define root as type Complex Double
. The data type Complex
is defined in the module Data.Complex
, and its type constructor is parameterized by a type parameter a
. In fact, the Complex
type is defined as follows:
data Complex a = a :+ a
There are several things to notice here. First, the type constructor of Complex
takes an argument a
. This is called type argument, as theÂ Complex
type can be constructed with any type a
.
The second thing to note is how the data constructor is defined. The data constructor's name is not alphanumeric, and it is allowed.
Note that the data constructor takes two parameters. In such a case, data constructor can be used with infix notation. That is, you can use the constructor in between two arguments.
The third thing to note is that the type parameter used in the type constructor can be used as a type while defining the data constructor.
Since our quadratic equation is defined in terms of Double
, the complex root will always have a type Complex Double
. Hence, we will define a type synonym using the following command:
type RootT = Complex Double
We will define two roots of the equation using the following command:
data Roots = Roots RootT RootT deriving Show
Here, we have not used the record syntax, but just decided to create two anonymous fields of type RootT
with data constructor Roots
.
The roots
Â function is defined as follows:
roots :: Quadratic > Roots
It can be interpreted as the roots
Â function has a type Quadratic > Roots
, which is a function that takes a value of type Quadratic
and returns a value of type Roots
:
 Pattern matching: We can write values byÂ exploding data constructor in the function arguments. Haskell matches these values and then calls the definition on the righthand side. In Haskell, we can separate the function definition using such matching. Here, we will use pattern matching to separate cases I, II, and III, defined in the preceding section. The case I can be matched with value (
Quadratic 0 0 _
) where the first two zeros match fieldsa
andb
, respectively. The last field is specified byÂ_
, which means that we do not care about this value, and it should not be evaluated.  Raising an error: For the first case, we flag an error by using function error. The function error takes a string and has a signature (
error :: String > a
) which means that it takes aString
and returns value of any typea
. Here, it raises an exception.  let .. in clause: In the case II as mentioned in the preceding section, we use
let ... in
clause.Â
let root = ( (c) / b :+ 0) in Roots root root
Here, theÂ let
clause is used to bind identifiers (which always start with a lowercase letter; so do function names). The let
clause is followed by theÂ in
clause. The in
clause has the expression that is the value of theÂ letâ€¦in
clause. The in
expression can use identifiers defined in let
. Furthermore, let
can bind multiple identifiers and can define functions as well.
We definedÂ rootsInternal
as a function to actually calculate the roots of a quadratic equation. The rootsInternal
functionÂ uses pattern guards. The pattern guards are explained as follows:
 Pattern guards: The pattern guards are conditions that are defined after a vertical bar

after the function arguments. The pattern guard defines a condition. If the condition is satisfied, then the expression on the righthand side is evaluated:
rootsInternal q d  d == 0 = ...
In the preceding definition, d == 0
defines the pattern guard. If this condition is satisfied, then the function definition is bound to the expression on the righthand side.
 where clause: The
rootsInternal
functionÂ also uses theÂwhere
clause. This is another form of theÂletâ€¦in
clause:
let <bindings> in <expression>
It translates to the following lines of code:
<expression> where <bindings>
In Main.hs
, we will import the Quadratic
module and use the functions and data type defined in it. We will use theÂ do syntax, which is used in conjunction with theÂ IO
type, for printing to the console, reading from the console, and, in general, for interfacing with the outside world.
The putStrLn
functionÂ prints the string to the console. The function converts a value to a string. This is enabled because of autodefinition due to deriving Show
.
We will use a data constructor to create values of Quadratic
. We can simply specify all the fields in the order such as, Quadratic 1 3 4
, where a = 1
, b = 3
, and c = 4
. We can also specify the value of Quadratic
using record syntax, such asÂ Quadratic { a = 10, b = 30, c = 5 }
.
Things are normally put in brackets, as shown here:
putStrLn (show (roots (Quadratic 0 1 2)))
However, in this case, we will use a special function $
, which simplifies the application of brackets and allows us to apply arguments to the function from right to left as shown:
putStrLn $ show $ roots (Quadratic 0 1 2)
You must have also noticed how the Haskell source code is formatted. The blocks are indented by white spaces. There is no hard and fast rule for indenting; however, it must be noted that there has to be a significant white space indent for a source code block, such as theÂ let
clause or theÂ where
clause. A simple guideline is that any block should be indented in such a way that it is left aligned and increases the readability of the code. A good Haskell source is easy to read.
In this recipe, we will work with theÂ list
data type in Haskell. TheÂ list
Â function is one of the most widely used data types in Haskell.
Use Stack to create a new project, list
functions, and change into the project directory. Build the project with stack build
.
Remove the contents of src/Lib.hs
Â and add the module heading:
module Lib where
 Create an empty list:
emptyList = []
 Prepend an element to the list:
prepend = 10 : []
 Create a list of five integers:
list5 = 1 : 2 : 3 : 4 : 5 : []
 Create a list of integers from
1
to10
:
list10 = [1..10]
 Create an infinite list:
infiniteList = [1..]
 This is the head of a list:
getHead = head [1..10]
 This is the tail of a list:
getTail = tail [1..10]
 This is all but the last element:
allbutlast = init [1..10]
 Take
10
elements:
take10 = take 10 [1..]
 Drop
10
elements:
drop10 = drop 10 [1..20]
 Get n^{th} element:
get1331th = [1..] !! 1331
 Check if a value is the element of the list.
is10element = elem 10 [1..10]
 Do pattern matching on the list. Here we check whether the list is empty or not:
isEmpty [] = True isEmpty _ = False
 Do more pattern matching. Here we do pattern matching on elements present in the list.Â
isSize2 (x:y:[]) = True isSize2 _ = False
 Concatenate two lists:
cat2 = [1..10] ++ [11..20]
 String is actually a list. Check this by creating a list of characters:
a2z = ['a'..'z']
 Since string is a list, we can use all list functions on string. Here we get the first character of a string:
strHead = head "abc"
 Zip two lists:
zip2 = zip ['a'..'z'] [1.. ]
 Open
app/Main.hs
and use the preceding functions from the list, and also print values:
module Main where import Lib main :: IO () main = do putStrLn $ show $ (emptyList :: [Int]) putStrLn $ show $ prepend putStrLn $ show $ list5 putStrLn $ show $ list10 putStrLn $ show $ getHead putStrLn $ show $ getTail putStrLn $ show $ allbutlast putStrLn $ show $ take10 putStrLn $ show $ drop10 putStrLn $ show $ get1331th putStrLn $ show $ is10element putStrLn $ show $ isEmpty [10] putStrLn $ show $ isSize2 [] putStrLn $ show $ cat2 putStrLn $ show $ a2z putStrLn $ show $ strHead
 Build the project using
stack build
and run it using stack runlistfunctionsexe
. Note thatÂMain
does not use theinfiniteList
snippets and does not print them.
List is defined as follows:
data [] a = []  Empty list or  a : [a]  An item prepended to a list, is also a list
There are two data constructors. The first data []
constructor shows an empty list, and a list with no elements is a valid list. The second data constructor tells us that an item prepended to a list is also a list.
Also, notice that the type constructor is parameterized by a type parameter a
. It means that the list can be constructed with any type a
.
The first three snippets in the previous sectionÂ are created using list's data constructors. The third example shows recursive application of the second constructor.
The fourth and fifth snippets show how to create a list from enumerated values. Enumerated values are those that implement type class Enum
Â and are implemented by ordered types such as Int
, Double
, Float
, Char
, and so on. The enumerated type allows us to specify a range using '..'
(for example, 1..10
, which means numerals 1
to 10
, Â including 10
). It is also possible to drop theÂ to value. For example, 1..
Â will create an infinite list. It is also possible to specify an increment by specifying consecutive values. For example, 1,3,..10
Â will expand toÂ 1,3,5,7,9
Â (note that the last value 10
is not part of it as it does not belong to the sequence).
From the definition of a list, any element, when prepended to a list, is also a list. For example, 1:[2,3]
is also a list. Here, 1
is called the head of the list, and 2
is called the tail of the list.
The functions head and tail return head and tail, respectively,Â of the list. The snippets 6 and 7 show an example of head and tail. Head has a signature Â head :: [a] > a
Â and tail has a signatureÂ :: [a] > [a]
Â .
Once we have a list, we can do various operations, such as the following ones:
init :: [a] > [a]
: Take all but the last element of the list. This is shown in snippet 8.take :: Int > [a] > [a]
: Take, at the most, the first n elements of the list (shown as theÂInt
argument). If the list has less than n elements, then it will consume the entire list. This is shown in snippet 9.In snippet 9, we worked on an infinite list and took only the first
10
elements. This works in Haskell, because in Haskell, nothing is evaluated until computation needs a value. Hence, even if we have an infinite list, when we take the first10
elements, only10
elements of the list are evaluated. Such things are not possible in strict languages. Haskell is not a strict language.drop :: Int > [a] > [a]
: Similar to take, but theÂdrop
function drops the first n elements. It will drop the whole list if the list has less than n elements. If we operate on an infinite list, then we will get an infinite list back. Snippet 10 shows an example of drop.
The function names in Haskell do not necessarily start with alphabets. Haskell allows us to use a combination of other characters as well. Many collections, including list, define !!
as an indexing
function. Snippet 11Â uses this.
The function !!
takes a list and an index n, and returns theÂ n^{th} element, starting 0
. The signature of !!
is Â (!!) :: Int > [a] > a
.
It is important to note that an access to an indexed element in the list is not random. It is sequential and is directly proportional to the index value. Hence, care should be taken to use this function.
The elem
Â function checks whether a given element is present in the list. The elem
Â function must be able to equate itself with another of its own type. This is done by implementing type Eq
Â class, which allows checking whether two values of a type are equal or not.
Once we know that a list has two data constructors, we can use them in the function argument for pattern matching. Hence, we can use []
for empty list matching, and we can useÂ x:y:[]
Â to match two elements followed by an empty list.
In the snippet 13, we used an empty list pattern for checking whether a list is empty or not.
In the snippet 14, we used x:y:[]
Â to check whether the list has length 2
or not. This might not be a very good thing if we want to check the larger size. There, we might use theÂ length
functionÂ to get the size of the list. However, be aware of the fact that the length
functionÂ is not a constant time function, but proportional to the size of the list.
It is possible to concatenate two lists by using the ++
Â function.Â The running time of this function is directly proportional to the size of the first list.
It is important to note that the type String
in Haskell is implemented as a list of Char
:
type String = [Char]
Hence, all list operations are valid string operations as well. The snippets 17 and 18 show this by applying list
Â functions onÂ String
. Since list is not a random access collection and operations such as concatenation are not constant time operations, strings in Haskell are not very efficient. There are libraries such as text that implement strings in a very efficient way.