ECS713-week06
Dr. Paulo Oliva / Prof. Edmund Robinson
Week 6: Input/Output in Haskell
ECS713
Functional Programming
Core Haskell / Types / IO Actions / Haskell “stack” Tool / Parsing
Core Haskell: Working with modules
– import modules using the “import” keyword
– organise your code into modules using the “module” keyword
Types: The “unit” type ()
– understand the role the unit type plays when working with IO actionsWeek 6/Task 1
IO Actions: Pure vs impure programming
– define the notion of computational “side effect”
– understand the difference between “pure” (no side effects) and “impure” (with side effects) programs
IO Actions: The IO type function
– familar with the basic Haskell functions that have IO type: print, putStrLn, readLnWeek 6/Task 1
– understand how impure programming in Haskell is controlled via the IO type functionWeek 6/Task 1
IO Actions: The “do” notation
– understand how IO action can be combined using the “do” notationWeek 6/Task 1
IO Actions: Working with files
– read contents from a text file (readFile), and and write to a text file (writeFile)Week 6/Task 1
Haskell “stack” Tool: Basic stack usage
– create a new project using “stack new”
– build an executable using “stack build”
Parsing: Recursive descent parsing
– understand the concept of “recursive descent parsing” and how it can be used to parse data defined
via context-free grammars
http://learnyouahaskell.com/modules#loading-modules
http://learnyouahaskell.com/modules#making-our-own-modules
https://en.wikipedia.org/wiki/Unit_type
https://livebook.manning.com/book/functional-programming-in-c-sharp/chapter-2/12
https://livebook.manning.com/book/functional-programming-in-c-sharp/chapter-2/12
http://learnyouahaskell.com/input-and-output
http://learnyouahaskell.com/input-and-output
http://learnyouahaskell.com/a-fistful-of-monads#do-notation
http://learnyouahaskell.com/input-and-output#files-and-streams
https://docs.haskellstack.org/en/stable/README/#quick-start-guide
https://docs.haskellstack.org/en/stable/README/#quick-start-guide
https://www.cmi.ac.in/~spsuresh/teaching/prgh15/papers/monadic-parsing.pdf
https://www.cmi.ac.in/~spsuresh/teaching/prgh15/papers/monadic-parsing.pdf
https://www.cmi.ac.in/~spsuresh/teaching/prgh15/papers/monadic-parsing.pdf
stack
stack
• Cross-platform program for Haskell projects
• Tackle common build issues in Haskell
• Around since June of 2015
• A .cabal file for each package defines package-
level metadata (stack uses cabal)
• A stack.yaml file provides information on where
dependencies come from
stack – basic usage
$ stack new helloworld new-template
Create new project:
$ stack setup
Configure new project: (cd into new folder)
$ stack build
Build executable:
$ stack exec helloworld-exe
Run your program
stack – basic usage
$ stack new helloworld
Create new project (omit template for standard
project):
$ stack setup
Stack setup installs the appropriate ghc for the
project.
$ stack build
Build executable:
$ stack ghci
Run ghci
stack – project layout
Edmunds-MacBook-Pro:tmp edmundr$ tree demo
demo
├── ChangeLog.md
├── LICENSE
├── README.md
├── Setup.hs
├── app
│ └── Main.hs
├── demo.cabal
├── package.yaml
├── src
│ └── Lib.hs
├── stack.yaml
└── test
└── Spec.hs
3 directories, 10 files
Main Program
Project library files
Library module file
Directory for test
suite
stack – build files
Edmunds-MacBook-Pro:tmp edmundr$ tree demo
demo
├── ChangeLog.md
├── LICENSE
├── README.md
├── Setup.hs
├── app
│ └── Main.hs
├── demo.cabal
├── package.yaml
├── src
│ └── Lib.hs
├── stack.yaml
└── test
└── Spec.hs
3 directories, 10 files
cabal package
stack project
.yaml vs .cabal
• A project can have multiple packages
• Each project has a stack.yaml
• Each package has a .cabal file
• The .cabal file specifies which packages are
dependencies
• stack.yaml specifies which packages are available
• .cabal specifies the components, modules, and
build flags provided by a package
• stack.yaml specifies which packages to include
.Modules, packages, projects
• A module is a single source file
• It contains declarations, and an export list
• The name of the module is basically the name
of the file
• Modules are the units Haskell uses to import
code into programs
• The Haskell language knows about modules,
but not packages or projects
• Module: Haskell
• Package: Cabal
• Project: Stack
Creating Modules
— file: CrawlerDB.hs
module CrawlerDB ( printURLs ) where
import CrawlerHTTP
import Database.HDBC
import Database.HDBC.Sqlite3
printURLs :: IO ()
printURLs = do urls <- getURLs
mapM_ print urls
getURLs :: IO [URL]
getURLs = do conn <- connectSqlite3 "urls.db"
res <- quickQuery' conn "SELECT url FROM urls" []
return $ map fromSql (map head res)
module name should be
the same as file name
exported functions
function only
used locally
Using Modules
-- file: CrawlerDB.hs
module CrawlerDB ( printURLs ) where
import CrawlerHTTP
import Database.HDBC as Db
import Database.HDBC.Sqlite3 (connectSQlite3)
printURLs :: IO ()
printURLs = do urls <- getURLs
mapM_ print urls
getURLs :: IO [URL]
getURLs = do conn <- connectSqlite3 "urls.db"
res <- quickQuery' conn "SELECT url FROM urls" []
return $ map fromSql (map head res)
modules are imported
through import
directives
rename module
import list
Importing Modules
import Data.List -- import all
import Data.List (words, sort) -- selective import
import Data.List hiding (sort) -- import all except sort
Dealing with name clashes
import qualified Data.Map -- call as Data.Map.filter
import qualified Data.Map as M -- call as M.filter
.Modules, packages, projects
• A package is a library of Haskell modules known
to the compiler
• Cabal is a system for building and packaging
Haskell libraries and programs.
• The build of each package is controlled by a
“cabal” file.
• Packages have to be installed and registered in a
database in order to be usable by the compiler.
• Packages are identified by a base name and
version number:
• The Haskell language knows about modules, but
not packages or projects
• Module: Haskell
• Package: Cabal
• Project: Stack
.Modules, packages, projects
• Module: Haskell
• Package: Cabal
• Project: Stack
.Modules, packages, projects
• Module: Haskell
• Package: Cabal
• Project: Stack
.Modules, packages, projects
• Stack deals with projects
• A project can contain multiple packages, but
may just contain one.
• The build of a project is controlled by a
“stack.yaml” file.
• The stack.yaml file specifies which packages to
build, and where to get library files from if
they are needed.
• Module: Haskell
• Package: Cabal
• Project: Stack
stack.yaml
# file stack.yaml
...
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: ghc-7.10.2
resolver: lts-3.5
...
# Packages to be pulled from upstream that are not in the resolver
extra-deps:
- HTTP-4000.3.3
http://docs.haskellstack.org/en/stable/yaml_configuration/
resolver containing
curated package
set
package and
version
helloworld.cabal
name: helloworld
version: 0.1.0.0
...
library
hs-source-dirs: src
exposed-modules: Lib
build-depends: base >= 4.7 && < 5
, text
, HTTP
default-language: Haskell2010
...
source-repository head
type: git
location: https://github.com/githubuser/helloworld
https://docs.haskellstack.org/en/stable/GUIDE/#stackyaml-vs-cabal-files
package names,
sometimes with
version constraints
stack on ITL
• Use Linux
• First time run
$ stack-check
• Will create symbolic link
/home/USER/.stack
-> /import/scratch/ECS713P_stack
• From there on use stack as “normal”
Input/Output in Haskell
Pure versus Impure
Pure Impure
Definitions Commands
Stateless State (e.g. global variables)
No side effects Side effects
Easy to reason
about program
Easy to interact with
outside world
Pure versus Impure
Pure Impure
Definitions Commands
Stateless State (e.g. global variables)
No side effects Side effects
Easy to reason
about program
Easy to interact with
outside world
Pure versus Impure
Pure Impure
Definitions Commands
Stateless State (e.g. global variables)
No side effects Side effects
Easy to reason
about program
Easy to interact with
outside world
Contained in IO
types
Algebraic Types
Just like functions, our algebraic type can
be parametrised by another type
data Maybe a = Nothing | Just a
data Either a b = Left a | Right b
— Maybe is often used when a value might not be available
first :: [a] -> Maybe a
first [] = Nothing
first (x:xs) = Just x
You call these “type functions”
RE
CA
P
The IO Type Function
IO a
We call the type “IO a” an “IO action of type a”.
When performed it might carry out an action with
side-effect and yield a result of type a.
IO Int IO Bool IO [Char] IO ()
For instance, we could have: the “unit” type
IO is a (special) type function
The unit type ( )
Prelude> :type ()
() :: ()
() is the type containing only
one element, namely ()
It plays the role of “void” in
languages such as C and Java
Hence, a function that is only meant
to do an IO action but not return any
value will have return type “IO ()”
Prelude> :type print
print :: Show a => a -> IO ()
The IO Type Function
IO type is used for operations that
interact with the “outside world”
Prelude> :type print
print :: Show a => a -> IO ()
Prelude> :type readFile
readFile :: FilePath -> IO String
Prelude> :type getLine
getLine :: IO String
given something that can
be “shown”, shows it on
screen and returns ()
given a file path, reads
contents of the file and
returns this content
reads one line of user
input and returns that
list of characters
The IO Type Removal Question
IO type is used for operations that
interact with the “outside world”
Prelude> getLine
hello
“hello”
Prelude> length getLine
length only applies
to pure Strings
getLine
is an
IO String
What function can I apply to my IO String to
extract the String???
The IO Type Removal Answer
Prelude> getLine
hello
“hello”
Prelude> length getLine
length only applies
to pure Strings
getLine
is an
IO String
What function can I apply to my IO String to
extract the String???
Answer: there isn’t one
And that is deliberate!
The IO Type Removal Answer
Prelude> getLine
hello
“hello”
Prelude> length getLine
length only applies
to pure Strings
getLine
is an
IO String
What function can I apply to my IO String to
extract the String???
Anything acquired through an IO action stays in
IO type
It is forever tainted!
But you can unpack it locally inside a do
block.
Greeting Example
— file: week06.hs
main = do
putStrLn “Greetings! What is your name?”
x <- getLine
putStrLn $ "Welcome to Haskell, " ++ x ++ "!"
$ runghc week06.hs
Greetings! What is your name?
Paulo
Welcome to Haskell, Paulo!
“do” glues IO actions together
type of the whole action is the
type of the last action
I/O Actions
x <- getLine
1. perform this action
2. bind this name to
the returning value
The “do” block automatically
extracts the value of the
last action and returns that
as its own result
readFile / writeFile
import System.IO
import Data.Char
main = do
contents <- readFile "poem.txt"
writeFile "poem-cap.txt" (map toUpper contents)
To be or not to be
that’s the question
poem.txt
TO BE OR NOT TO BE
THAT’S THE QUESTION
poem-cap.txt
readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()