
If you have arrived here by https://github.com/data61/fp-course and you are looking for the answers (not the exercises), please go to https://github.com/tonymorris/fp-course
As of February 2017, this repository is taking the place of the repository hosted at https://github.com/NICTA/course which is deprecated.
Data61 replaces what was NICTA since July 2016. The new repository is located at https://github.com/data61/fp-course.
The course is structured according to a linear progression and uses the Haskell programming language to learn programming concepts pertaining to functional programming.
Exercises are annotated with a comment containing the word "Exercise." The existing code compiles, however answers have
been replaced with a call to the Haskell error function and so the code will throw an exception if it is run. Some
exercises contain tips, which are annotated with a preceding "Tip:". It is not necessary to adhere to tips. Tips are
provided for potential guidance, which may be discarded if you prefer a different path to a solution.
The exercises are designed in a way that requires personal guidance, so if you attempt it on your own and feel a little lost, this is normal. All the instructions are not contained herein.
There are two mailing lists for asking questions. All questions are welcome, however, your first post might be moderated. This is simply to prevent spam.
[nicta-fp] is a Google Group for any queries related to functional programming. This mailing list is owned by Data61 and is open to the public. Questions relating to this course are most welcome here.
[haskell-exercises] is a Google Group for queries related specifically to this Data61 functional programming course material. This mailing list is not owned by Data61, but is run by others who are keen to share ideas relating to the course.
#nicta-course on Freenode is an IRC channel that is operated by others who are going through this course material on their own time and effort.
#qfpl on Freenode is the IRC channel of the Queensland Functional Programming Lab - the team that runs the course in Brisbane.
#scalaz on Freenode is an IRC channel that is operated by others who are keen to share ideas relating to functional programming in general. Most of the participants of this channel have completed the Data61 functional programming course to some extent. They are in various timezones and share a passion for functional programming, so may be able to provide relatively quick assistance with questions.
NOTE If you do not wish to install these dependencies, you may use a virtual machine instead. Instructions for automatically building a virtual machine are available in this repository for your convenience.
Install the Glasgow Haskell Compiler (GHC) version 7.10 or higher.
Change to the directory containing this document.
Execute the command ghci, which will compile and load all the source code.
You may need to set permissions on the root directory and the ghci configuration
file, chmod go-w .ghci ./.
Inspect the introductory modules to get a feel for Haskell's syntax, then move
on to the exercises starting with Course.Optional. The
Progression section of this document lists the recommended
order in which to attempt the exercises.
Edit a source file to a proposed solution to an exercise. At the ghci
prompt, issue the command :reload. This will compile your solution and
reload it in the GHC interpreter. You may use :r for short.
They are all answerable with the :info command. For example, suppose you
have the question, "What does the swiggletwoop function mean?" You may
answer this at GHCi with:
> :info swiggletwoop
You may also use :i for short.
Functional Programming techniques rely heavily on types. This reliance may
feel foreign at first, however, it is an important part of this course. If
you wish to know the type of an expression or value, use :type. For
example,
> :type reverse
List t -> List t
This tells you that the reverse function takes a list of elements of some
arbitrary type (t) and returns a list of elements of that same type. Try
it.
You may also use :t for short.
GHCi has TAB-completion. For example you might type the following:
> :type rev
Now hit the TAB key. If there is only one function in scope that begins with
the characters rev, then that name will auto-complete. Try it. This
completion is context-sensitive. For example, it doesn't make sense to ask
for the type of a data type itself, so data type names will not auto-complete
in that context, however, if you ask for :info, then they are included in
that context. Be aware of this when you use auto-complete.
This also works for file names:
> readFile "/etc/pas"
Now hit the TAB key. If there is only one existing filename on a path that
begins with /etc/pas, then that name will auto-complete. Try it.
If there is more than one identifier that can complete, hit TAB twice quickly. This will present you with your options to complete.
Follow the types.
You may find yourself in a position of being unsure how to proceed for a given exercise. You are encouraged to adopt a different perspective. Instead of asking how to proceed, ask how you might proceed while adhering to the guideline provided by the types for the exercise at hand.
It is possible to follow the types without achieving the desired goal, however, this is reasonably unlikely at the start. As you become more reliant on following the types, you will develop more trust in the potential paths that they can take you, including identification of false paths.
Your instructor must guide you where types fall short, but you should also take the first step. Do it.
Do not use tab characters
Set up your text editor to use space characters rather than tabs. Using tab characters in Haskell can lead to confusing error messages. GHC will give you a warning if your program contains a tab character.
Do not use the stack build tool. It does not work.
Tests are available as a tasty test suite.
Tasty tests are stored under the test/ directory. Each module from the course
that has tests has a corresponding <MODULE>Test.hs file. Within each test
module, tests for each function are grouped using the testGroup function.
Within each test group there are test cases (testCase function), and
properties (testProperty function).
Before running the tests, ensure that you have an up-to-date installation of GHC and cabal-install from your system package manager or use the minimal installers found at haskell.org.
To run the full test suite, build the project as follows:
> cabal update
> cabal install --only-dependencies --enable-tests
> cabal configure --enable-tests
> cabal build
> cabal test
Tasty will also allow you to run only those tests whose description match a
pattern. Tests are organised in nested groups named after the relevant module
and function, so pattern matching should be intuitive. For example, to run the
tests for the List module you could run:
> cabal test tasty --show-detail=direct --test-option=--pattern="Tests.List."
Likewise, to run only the tests for the headOr function in the List module, you could use:
> cabal test tasty --show-detail=direct --test-option=--pattern="List.headOr"
In addition, GHCi may be used to run tasty tests. Assuming you have run ghci
from the root of the project, you may do the following. Remember that GHCi has
tab completion, so you can save yourself some typing.
> -- import the defaultMain function from Tasty - runs something of type TestTree
> import Test.Tasty (defaultMain)
>
> -- Load the test module you'd like to run tests for
> :l test/Course/ListTest.hs
>
> -- Browse the contents of the loaded module - anything of type TestTree
> -- may be run
> :browse Course.ListTest
>
> -- Run test for a particular function
> defaultMain headOrTest
The doctest tests are a mirror of the tasty tests that reside in comments
alongside the code. They are not executable, but examples can be copied into
GHCI. Examples begin with >>> while properties begin with prop>.
It is recommended to perform some exercises before others. The first step is to inspect the introduction modules.
Course.ExactlyOneCourse.ValidationThey contain examples of data structures and Haskell syntax. They do not contain
exercises and exist to provide a cursory examination of Haskell syntax. The next
step is to complete the exercises in Course.Optional.
After this, the following progression of modules is recommended:
Course.ListCourse.FunctorCourse.ApplicativeCourse.MonadCourse.FileIOCourse.StateCourse.StateTCourse.ExtendCourse.ComonadCourse.ComposeCourse.TraversableCourse.ListZipperCourse.Parser (see also Course.Person for the parsing rules)Course.MoreParserCourse.JsonParserCourse.InteractiveCourse.AnagramsCourse.FastAnagramsCourse.ChequeDuring this progression, it is often the case that some exercises are abandoned
due to time constraints and the benefit of completing some exercises over
others. For example, in the progression, Course.Functor to Course.Monad, the
exercises repeat a similar theme. Instead, a participant may wish to do
different exercises, such as Course.Parser. In this case, the remaining
answers are filled out, so that progress on to Course.Parser can begin
(which depends on correct answers up to Course.Monad). It is recommended to
take this deviation if it is felt that there is more reward in doing so.
Answers for the exercises can be found here: https://github.com/tonymorris/fp-course
After these are completed, complete the exercises in the projects directory.
If you choose to use the Leksah IDE for Haskell, the following tips are recommended:
./leksah-nix.sh ghc822
as the Nix files for this course use GHC 8.2.2.Build in the background and report errors ON - unless you prefer to triger builds
manualy with Ctrl + B to build (Command + B on OS X)Use GHC to compile ONUse GHCJS to compile OFFUse GHCi debugger to build and run ONMake documentation while building OFFRun unit tests when building ONRun benchmakrs when building OFFMake dependent packages ONnix-shell to build an environment for running the builds in. If nix-shell has
not been run before for the fp-course repo it may take some time to complete.
When it is finished a line of green '-' characters should be printed in the Panes -> Log.This section is a guide for the instructor to introduce Haskell syntax. Each of these points should be covered before attempting the exercises.
:: reads as has the type
-> in a type signature is right-associative\ symbol in a lambda expression denotes a Greek lambdabackticksdata keyword
data keyword is the data type name= sign(:)(|)deriving keyword gives us default implementations for some functions
on that data type= we are pattern-matching= we are constructingWhen this course is run in-person, some tools, particularly within Haskell, are covered first.
:type:infox :: T is read as x is of the type TisAlpha are prefix by default, infix surrounded in backticks (`)data keywordderiving keywordcabal testThe exercises in Parser.hs can be assisted by stating problems in a specific way, with a conversion to code.
| English | Parser library |
|---|---|
| and then | bindParser >>= |
| always | valueParser pure |
| or | ||| |
| 0 or many | list |
| 1 or many | list1 |
| is | is |
| exactly n | thisMany n |
| fail | failed |
| call it x | \x -> |
do>>= into <-->\<-from on each line>>= into in->\inselectAre these two programs, the same program?
p1 ::
IO ()
p1 =
let file = "/tmp/file"
in do _ <- writeFile file "abcdef"
x <- readFile file
_ <- putStrLn x
_ <- writeFile file "ghijkl"
y <- readFile file
putStrLn (show (x, y))
p2 ::
IO ()
p2 =
let file = "/tmp/file"
expr = readFile file
in do _ <- writeFile file "abcdef"
x <- expr
_ <- putStrLn x
_ <- writeFile file "ghijkl"
y <- expr
putStrLn (show (x, y))
What about these two programs?
def writeFile(filename, contents):
with open(filename, "w") as f:
f.write(contents)
def readFile(filename):
contents = ""
with open(filename, "r") as f:
contents = f.read()
return contents
def p1():
file = "/tmp/file"
writeFile(file, "abcdef")
x = readFile(file)
print(x)
writeFile(file, "ghijkl")
y = readFile(file)
print (x + y)
def p2():
file = "/tmp/file"
expr = readFile(file)
writeFile(file, "abcdef")
x = expr
print(x)
writeFile(file, "ghijkl")
y = expr
print (x + y)
Sometimes this course material is condensed into one-day. In these cases, the following exercises are recommended:
Optional
mapOptionalbindOptional(??)(<+>)List
headOrproductlengthmapfilter(++)flatMapreverseFunctor
instance Functor Listinstance Functor Optionalinstance Functor ((->) t)instance Functor voidApplicative
instance Applicative Listinstance Applicative Optionalinstance Applicative ((->) t)lift2sequenceFileIO(top 30 of 43)
Haskell
88.4%
Java
4.6%
Scala
2.9%
Idris
2.0%

If you have arrived here by https://github.com/data61/fp-course and you are looking for the answers (not the exercises), please go to https://github.com/tonymorris/fp-course
As of February 2017, this repository is taking the place of the repository hosted at https://github.com/NICTA/course which is deprecated.
Data61 replaces what was NICTA since July 2016. The new repository is located at https://github.com/data61/fp-course.
The course is structured according to a linear progression and uses the Haskell programming language to learn programming concepts pertaining to functional programming.
Exercises are annotated with a comment containing the word "Exercise." The existing code compiles, however answers have
been replaced with a call to the Haskell error function and so the code will throw an exception if it is run. Some
exercises contain tips, which are annotated with a preceding "Tip:". It is not necessary to adhere to tips. Tips are
provided for potential guidance, which may be discarded if you prefer a different path to a solution.
The exercises are designed in a way that requires personal guidance, so if you attempt it on your own and feel a little lost, this is normal. All the instructions are not contained herein.
There are two mailing lists for asking questions. All questions are welcome, however, your first post might be moderated. This is simply to prevent spam.
[nicta-fp] is a Google Group for any queries related to functional programming. This mailing list is owned by Data61 and is open to the public. Questions relating to this course are most welcome here.
[haskell-exercises] is a Google Group for queries related specifically to this Data61 functional programming course material. This mailing list is not owned by Data61, but is run by others who are keen to share ideas relating to the course.
#nicta-course on Freenode is an IRC channel that is operated by others who are going through this course material on their own time and effort.
#qfpl on Freenode is the IRC channel of the Queensland Functional Programming Lab - the team that runs the course in Brisbane.
#scalaz on Freenode is an IRC channel that is operated by others who are keen to share ideas relating to functional programming in general. Most of the participants of this channel have completed the Data61 functional programming course to some extent. They are in various timezones and share a passion for functional programming, so may be able to provide relatively quick assistance with questions.
NOTE If you do not wish to install these dependencies, you may use a virtual machine instead. Instructions for automatically building a virtual machine are available in this repository for your convenience.
Install the Glasgow Haskell Compiler (GHC) version 7.10 or higher.
Change to the directory containing this document.
Execute the command ghci, which will compile and load all the source code.
You may need to set permissions on the root directory and the ghci configuration
file, chmod go-w .ghci ./.
Inspect the introductory modules to get a feel for Haskell's syntax, then move
on to the exercises starting with Course.Optional. The
Progression section of this document lists the recommended
order in which to attempt the exercises.
Edit a source file to a proposed solution to an exercise. At the ghci
prompt, issue the command :reload. This will compile your solution and
reload it in the GHC interpreter. You may use :r for short.
They are all answerable with the :info command. For example, suppose you
have the question, "What does the swiggletwoop function mean?" You may
answer this at GHCi with:
> :info swiggletwoop
You may also use :i for short.
Functional Programming techniques rely heavily on types. This reliance may
feel foreign at first, however, it is an important part of this course. If
you wish to know the type of an expression or value, use :type. For
example,
> :type reverse
List t -> List t
This tells you that the reverse function takes a list of elements of some
arbitrary type (t) and returns a list of elements of that same type. Try
it.
You may also use :t for short.
GHCi has TAB-completion. For example you might type the following:
> :type rev
Now hit the TAB key. If there is only one function in scope that begins with
the characters rev, then that name will auto-complete. Try it. This
completion is context-sensitive. For example, it doesn't make sense to ask
for the type of a data type itself, so data type names will not auto-complete
in that context, however, if you ask for :info, then they are included in
that context. Be aware of this when you use auto-complete.
This also works for file names:
> readFile "/etc/pas"
Now hit the TAB key. If there is only one existing filename on a path that
begins with /etc/pas, then that name will auto-complete. Try it.
If there is more than one identifier that can complete, hit TAB twice quickly. This will present you with your options to complete.
Follow the types.
You may find yourself in a position of being unsure how to proceed for a given exercise. You are encouraged to adopt a different perspective. Instead of asking how to proceed, ask how you might proceed while adhering to the guideline provided by the types for the exercise at hand.
It is possible to follow the types without achieving the desired goal, however, this is reasonably unlikely at the start. As you become more reliant on following the types, you will develop more trust in the potential paths that they can take you, including identification of false paths.
Your instructor must guide you where types fall short, but you should also take the first step. Do it.
Do not use tab characters
Set up your text editor to use space characters rather than tabs. Using tab characters in Haskell can lead to confusing error messages. GHC will give you a warning if your program contains a tab character.
Do not use the stack build tool. It does not work.
Tests are available as a tasty test suite.
Tasty tests are stored under the test/ directory. Each module from the course
that has tests has a corresponding <MODULE>Test.hs file. Within each test
module, tests for each function are grouped using the testGroup function.
Within each test group there are test cases (testCase function), and
properties (testProperty function).
Before running the tests, ensure that you have an up-to-date installation of GHC and cabal-install from your system package manager or use the minimal installers found at haskell.org.
To run the full test suite, build the project as follows:
> cabal update
> cabal install --only-dependencies --enable-tests
> cabal configure --enable-tests
> cabal build
> cabal test
Tasty will also allow you to run only those tests whose description match a
pattern. Tests are organised in nested groups named after the relevant module
and function, so pattern matching should be intuitive. For example, to run the
tests for the List module you could run:
> cabal test tasty --show-detail=direct --test-option=--pattern="Tests.List."
Likewise, to run only the tests for the headOr function in the List module, you could use:
> cabal test tasty --show-detail=direct --test-option=--pattern="List.headOr"
In addition, GHCi may be used to run tasty tests. Assuming you have run ghci
from the root of the project, you may do the following. Remember that GHCi has
tab completion, so you can save yourself some typing.
> -- import the defaultMain function from Tasty - runs something of type TestTree
> import Test.Tasty (defaultMain)
>
> -- Load the test module you'd like to run tests for
> :l test/Course/ListTest.hs
>
> -- Browse the contents of the loaded module - anything of type TestTree
> -- may be run
> :browse Course.ListTest
>
> -- Run test for a particular function
> defaultMain headOrTest
The doctest tests are a mirror of the tasty tests that reside in comments
alongside the code. They are not executable, but examples can be copied into
GHCI. Examples begin with >>> while properties begin with prop>.
It is recommended to perform some exercises before others. The first step is to inspect the introduction modules.
Course.ExactlyOneCourse.ValidationThey contain examples of data structures and Haskell syntax. They do not contain
exercises and exist to provide a cursory examination of Haskell syntax. The next
step is to complete the exercises in Course.Optional.
After this, the following progression of modules is recommended:
Course.ListCourse.FunctorCourse.ApplicativeCourse.MonadCourse.FileIOCourse.StateCourse.StateTCourse.ExtendCourse.ComonadCourse.ComposeCourse.TraversableCourse.ListZipperCourse.Parser (see also Course.Person for the parsing rules)Course.MoreParserCourse.JsonParserCourse.InteractiveCourse.AnagramsCourse.FastAnagramsCourse.ChequeDuring this progression, it is often the case that some exercises are abandoned
due to time constraints and the benefit of completing some exercises over
others. For example, in the progression, Course.Functor to Course.Monad, the
exercises repeat a similar theme. Instead, a participant may wish to do
different exercises, such as Course.Parser. In this case, the remaining
answers are filled out, so that progress on to Course.Parser can begin
(which depends on correct answers up to Course.Monad). It is recommended to
take this deviation if it is felt that there is more reward in doing so.
Answers for the exercises can be found here: https://github.com/tonymorris/fp-course
After these are completed, complete the exercises in the projects directory.
If you choose to use the Leksah IDE for Haskell, the following tips are recommended:
./leksah-nix.sh ghc822
as the Nix files for this course use GHC 8.2.2.Build in the background and report errors ON - unless you prefer to triger builds
manualy with Ctrl + B to build (Command + B on OS X)Use GHC to compile ONUse GHCJS to compile OFFUse GHCi debugger to build and run ONMake documentation while building OFFRun unit tests when building ONRun benchmakrs when building OFFMake dependent packages ONnix-shell to build an environment for running the builds in. If nix-shell has
not been run before for the fp-course repo it may take some time to complete.
When it is finished a line of green '-' characters should be printed in the Panes -> Log.This section is a guide for the instructor to introduce Haskell syntax. Each of these points should be covered before attempting the exercises.
:: reads as has the type
-> in a type signature is right-associative\ symbol in a lambda expression denotes a Greek lambdabackticksdata keyword
data keyword is the data type name= sign(:)(|)deriving keyword gives us default implementations for some functions
on that data type= we are pattern-matching= we are constructingWhen this course is run in-person, some tools, particularly within Haskell, are covered first.
:type:infox :: T is read as x is of the type TisAlpha are prefix by default, infix surrounded in backticks (`)data keywordderiving keywordcabal testThe exercises in Parser.hs can be assisted by stating problems in a specific way, with a conversion to code.
| English | Parser library |
|---|---|
| and then | bindParser >>= |
| always | valueParser pure |
| or | ||| |
| 0 or many | list |
| 1 or many | list1 |
| is | is |
| exactly n | thisMany n |
| fail | failed |
| call it x | \x -> |
do>>= into <-->\<-from on each line>>= into in->\inselectAre these two programs, the same program?
p1 ::
IO ()
p1 =
let file = "/tmp/file"
in do _ <- writeFile file "abcdef"
x <- readFile file
_ <- putStrLn x
_ <- writeFile file "ghijkl"
y <- readFile file
putStrLn (show (x, y))
p2 ::
IO ()
p2 =
let file = "/tmp/file"
expr = readFile file
in do _ <- writeFile file "abcdef"
x <- expr
_ <- putStrLn x
_ <- writeFile file "ghijkl"
y <- expr
putStrLn (show (x, y))
What about these two programs?
def writeFile(filename, contents):
with open(filename, "w") as f:
f.write(contents)
def readFile(filename):
contents = ""
with open(filename, "r") as f:
contents = f.read()
return contents
def p1():
file = "/tmp/file"
writeFile(file, "abcdef")
x = readFile(file)
print(x)
writeFile(file, "ghijkl")
y = readFile(file)
print (x + y)
def p2():
file = "/tmp/file"
expr = readFile(file)
writeFile(file, "abcdef")
x = expr
print(x)
writeFile(file, "ghijkl")
y = expr
print (x + y)
Sometimes this course material is condensed into one-day. In these cases, the following exercises are recommended:
Optional
mapOptionalbindOptional(??)(<+>)List
headOrproductlengthmapfilter(++)flatMapreverseFunctor
instance Functor Listinstance Functor Optionalinstance Functor ((->) t)instance Functor voidApplicative
instance Applicative Listinstance Applicative Optionalinstance Applicative ((->) t)lift2sequenceFileIO(top 30 of 43)
Haskell
88.4%
Java
4.6%
Scala
2.9%
Idris
2.0%