You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
isn't an imperative language (sequence of tasks, variable values can change, have control structure)
elegant and concise, its programs usually are short than their imperative equivalents
functions are pure:
functions has no side-effects, the only thing it can do is to calculate something and return a result
functions have referential transparency:
calling a function twice with the same arguments it's guaranteed to return the same result
replace a function with its corresponding value doesn't change the program's behavior (because a pure function doesn't have side-effects, only return a value)
it's lazy:
unless specifically told otherwise, Haskell won't execute functions and calculate things until it's really force to show you the result
this allow things such a infinite data structure
it's statically typed:
compiler knows which piece of code is a number, string and so on...
it includes type inference = the type system intelligently figure out the type of variables
Installation
full installation Haskell Plataform
sudo apt install haskell-plataform
sudo apt install haskell-stack && stack upgrade if weren't installed with command above
Run
GHC takes a Haskell script (usually '.hs' extension) and compile
GHCi - Interactive mode
GHCi is the GHC interactive mode
workflow: load a script, call a function from it and get results immediately
this is much easier and faster than compiling every time
commands
ghci = invoke the interactive mode
:l <filename>.hs = load a specific script
:r = to reload the script
:t, :type = get expression type (Char, Bool...) or function type/signature
e.g. :t sqrt, :t (+)
:info String
compile
# remember: 'main =' is the entry point and is required
ghc --make myfilename
./myfilename
runhaskell
runhaskell filename.hs = run on the fly
Packages
either cabal-install or stack, both overlap a lot but have different workflows
they're build tools and package managers
both use the Cabal library underneath, think of them like an frontend
recommendation: use any, doesn't matter unless you're creating a library which is required to work specifically with cabal-install and/or stack
cabal-install
files: '.cabal'
dependency problems (version conflicts in global installations) happened very often
cabal-install evolved in recent years:
introduced sandboxes (project specific installations)
dependency conflict avoidance was added
-- global installation
cabal update
cabal install <package-name>
Stack
replaces cabal-install
was created to solve cabal-install's very common dependency problems
files: '.cabal' plus a additional '.stack.yaml'
it uses the Cabal library but with a curated version of the Hackage called Stackage where dependencies are curated to build together
stack upgrade -- update
-- setup a new project
stack new my-project
cd my-project
stack setup
stack build
stack exec my-project-exe
stack install <package-name>
Modules
Export
-- module name (always capitalized) and filename must be the samemoduleMyModule (double) where
double x = x *2
-- optional-- use a colon to define a value type
a =12-- defaults to type double-- declaration on topb::Int
b =12-- inline declaration
c =3::Float
Maybe & Either
-- Maybe type is an instance of Functor and Monad-- data Maybe a = Nothing | Just a-- represents the potential absence of value; similar to null in JSmdouble:: [Int] ->Maybe [Int]
mdouble []=Nothing
mdouble x =Just (map (*2) x)
a = mdouble []
b = mdouble [3, 5]
-- represents a pice of data that can be one of two things-- Left value is used when something went wrong, Right when everything is oksafeDiv::Int->Int->EitherStringInt
safeDiv _ 0=Left"You cannot divide by zero!"
safeDiv x y =Right (x `div` y)
c = safeDiv 30
typeclasses
type class declaration
-- types have typeclasses; they're an instance of a typeclass-- everything before => is called class constraint-- :t (==) -- Eq a => a -> a -> Bool-- `(Num a)` = 'a' must be of numeric type (Int, Double...)getLength:: (Numa) => [a] ->Int
getLength []=0
getLength (_:xs) =1+ (getLength xs)
Show & Read
-- Show and Read are typeclasses-- an instance of Show os anything that can be represented as a string-- show function takes value (member of Show) and presents it as a stringshow3-- "3"-- read function takes a string and returns a type (member of Read)read (show"abc") ::String-- "abc"read"5"::Integer-- 5
Creating types & classes
enumerated types
-- enumerated type consist of a fixed set of constantsdataWeekend=Friday | Saturday | Sundayderiving (Show, Eq)
day1::Weekend
day1 =Friday-- print is only possible because it's derived from Show-- compare is only possible because is derived from Eq
compareMe = day1 ==Friday
-- instance gives more control than deriving-- an instance of a class is an individual type which belongs to that classdataTrafficLight=Red | Yellow | GreeninstanceEqTrafficLightwhereRed==Red=TrueYellow==Yellow=TrueGreen==Green=True
_ == _ =FalseinstanceShowTrafficLightwhereshowRed="Red light"showYellow="Yellow light"showGreen="Green light"x::TrafficLight
x =Redy::TrafficLight
y =Green
class
classYesNoawheretruth::a->Bool-- make a type be a instance of the created classinstanceYesNo [a] where
truth []=False
truth _ =True
a = truth [3, 5]
b = truth []
Functions
Create functions
addMe x y = x + y
type declarations
optional, but good practice
add::Int->Int->Int-- arguments: 2 Int, return: Int
add x y = x + y
listMax:: [Int] ->Int-- argument: list of Int, return: Int
listMax x =maximum x
-- test with max or minfiveOrTen:: (Int->Int->Int) ->Int-- argument: function (gets two Int, returns one)
fiveOrTen func = func 510
Infix vs prefix
2 * 3 = infix function
mod 10 3 = prefix function
convert:
(+) 2 3 = from infix to prefix
10 `mod` 3 = from prefix to infix
pattern matching
-- separate function bodies for different patterns-- only performs equality (=) comparisonsnum::Int->String
num 1="one"
num 2="two"
num _ ="neither one nor two"
Parentheses
used to do function application, just like the space (however, space has higher precedence)
arguments aren't surrounded by parentheses
are only required for function application; e.g. a (b 10)
dollar sign
-- function application, but with lower precedence than space-- it's a convenience function so that we don't have to write so many parentheses. -- when a $ is encountered, the expression on its right is applied as the parameter to the function on its left-- ($) :: (a -> b) -> a -> b
x =mod9$round2.0
Recursion
fact x = if x == 1 then x else x * fact (x - 1)
higher order functions
-- map :: (a -> b) -> [a] -> [b]-- map takes a function
x =map ((+)3) [7, 12, 15]
-- partially apply any function returns a function
double =(*)2
y = double 7
curry
-- every function in Haskell officially only takes one parameter-- function that accept several arguments are curried functions-- putting a space between two things is simply function application-- application with a space is left-associative ; f a b c = ((f a) b) c))-- max :: Ord a => a -> a -> a-- e.g. max takes an 'a', returns (->) a function that takes an 'a' and returns an 'a'
x =max73
partial application
-- because functions in Haskell are curried,-- when you call one with too few arguments,-- we get back a partially applied function
add3 =(+)3
x = add3 7-- 10
Lambda
x =map (\x -> x *2) [3, 5, 8] -- backslash denotes an lambda
composition, dot operator
-- (f . g) x = f(g(x))-- (f . g . z) x = f (g (z x))
pow2 x = x * x
half x = x /2
add2 x = x +2
a = (pow2 . half . add2) 12-- 49.0
b = (add2 . half . pow2) 12-- 74.0-- lambda
l =map (\x ->negate (abs x)) [-4, 2, -1, 3] -- without composition
ll =map (negate.abs) [-4, 2, -1, 3] -- with composition
Operators
operators are just functions with special names
Control structures
if
x = if True then "t" else "f" = if is an expression
else part is mandatory
because every expression and function must return something
case
-- case only performs equality (=) comparisons-- (not >, <, ...)num::Int->String
num n =case n of1->"one"2->"two"
_ ->"neither one nor two"-- syntactic sugar-- num :: Int -> String-- num 1 = "one"-- num 2 = "two"-- num _ = "neither one nor two"
guards
education::Int->String
education age
| age <=6="kindergarten"| age <=17="school"|otherwise="college or other"
where
-- where is used to avoid repetition--- names defined at where are only visible to that functionavgScore::Int->Int->String
avgScore n1 n2
| avg <60="low"| avg >=60="high"where avg = (n1 + n2) `div`2
where & case
isList:: [a] ->String
isList l ="The list is "++ what l
where what []="empty."
what [x] ="a singleton."
what xs ="a longer list."
let
-- similar to where-- let vs where = let is an expression and go in many more places than where-- let expression
a =let x =7in x +3-- inside list comprehension
b = [(x, y) | x <- [3, 5, 8], let y =2* x]
-- inside do-notation
c =dolet x =7
x +3
Data structures
character
enclosed in single quotes
Lists
homogeneous = it stores several elements of the same type
list of characters are displayed as a string (double quotes)
concatenation
[1, 2] ++ [3, 4, 5]
cons operator
0:[1,2,3] = cons operator = prepend a single element to list
cons takes a single number/char and a list, whereas concatenation takes two lists
get element (!!)
[1, 2, 3] !! 1 = 2
comparison
e.g. [2, 3] /= [2, 3, 4]
first the heads are compared. If they are equal then the second elements are compared, etc
ranges
[1..20]
product [1..8] = factorial of 8
[0, 2..10] = specific step; first two is the first to in the sequence and the last is the upper limit
[20, 19..0] = list with decreasing values, set a step is necessary
infinite lists & laziness
[0, 2..] = doesn't specify upper limit, infinite list
take 10 [0, 2..] = take is to make sure that the program don't run indefinitely
list comprehension
returns a list from an existent list(s)
[x * 2 | x <- [1..10], mod x 2 == 0] = [2,4,6,8,10,12,14,16,18,20]
x <- [1..10] = generator; can have multiple generators
x * 2 = output function
mod x 2 == 0 = predicate or condition; only numbers that pass this condition, will be passed to the output function
Strings
enclosed in double quotes
are just lists of characters
"hello" is just syntactic sugar for ['h','e','l','l','o']
Tuples
[("a", True), ("b", False), ("c", True)] = a list containing 3 tuples size 2
fixed-length of values
its type depends on how many components it has and the types of the components
a tuple size 2 has a different type of a tuple size 3
("a", True) and ("b", 3) have different types
access in tuples with 2 items: fst first el, snd second el
Pattern matching
as pattern
-- break something up and keep a: reference to the wholebaz::String->String
baz all@(x:xs) ="The first letter of '"++all++"' is "++ [x]
Lists (xs)
double:: [Int] -> [Int]
double []=[]
double (x:xs) = x *2: double xs -- x is the first element, xs is the rest
I/O
Haskell separates the part of our program that is pure and the part that is impure
I/O actions will only be performed when they are
given a name of main
and/or inside a bigger I/O action that we composed with a do block
'<-' gets the result from a I/O action (e.g. IO String) and stores it as a normal value
main =do-- 'do' is to perform multiple I/O actionsputStr ("What's your name? ")
name <-getLine-- note '<-' instead of '='putStrLn ("Hello, "++ name ++"!")
let
importData.Char
main =doputStrLn"What's your name?"
name <-getLinelet upperName =map toUpper name -- let is needed to declare name inside mainputStrLn ("Hello, "++ upperName ++"!")
return
return is different in Haskell, it doesn't ends the execution of a function
it's the opposite of '<-', it makes an I/O action out of a pure value
it's usually used to create an I/O action that doesn't do anything or to reset the previous I/O action value
main =do
a <-return"test"-- creates a I/O value (like getLine) and doesn't end executionputStrLn$ a
main recursion
main =do
line <-getLineifnull line
thenreturn()-- does nothingelsedoputStrLn line
main -- recursively call itself
print text
main =doputStr"without line break; "putStrLn"with line break"putStrLn (show25) -- doesn't work without show because it's not a stringprint25
files
hGetContents
importSystem.IO
main =do
handle <- openFile "test.txt"ReadMode-- WriteMode | ReadWriteMode | AppendMode
contents <- hGetContents handle
putStrLn contents
hClose handle -- best practice, make sure to free resources and flush data
utils
concise functions to open, read and edit files
readFile, writeFile & appendFile
importSystem.IO
main =do
contents <-readFile"test.txt"putStrLn contents
writeFile"test.txt""content here..."-- rewriteappendFile"test.txt""one more line\n"
Functors & monads
functors
in Haskell a functor is a type (with typeclass of Functor) that can be mapped over (e.g. lists, maps, trees)
functors = things that can be mapped over
described by the typeclass Functor which has only one method (fmap)
by mapping multi-parameter functions over functors, we get functors that contain functions inside them
fmap
infix version: '<$>'; =
fmap is a generalized representation of map (map is specific to lists)
fmap :: (a -> b) -> f a -> f b; reads:
give me a function that takes an 'a' and returns a 'b' and a box with an 'a' (or several of them) inside it
and I'll return you a box with a 'b' (or several of them) inside it.
'f' is a parameterized data type (e.g. List) that takes a type parameter (e.g. Int)
-- <$> = infix version of fmap
a =fmap (\x -> x *2) [3, 5, 8]
b = (\x -> x *2) <$> [3, 5, 8]
applicative functors
-- structure intermediate between a functor and a monad-- found in the Applicative typeclass; contains 'pure' & '<*>' function-- <*> has the same type declaration than fmap and it's a more robust version of fmap-- fmap takes a function and a functor and applies the function inside the functor-- <*> takes a functor that has a function in it and another functor, extracts the function from the first functor and then maps it over the second one-- by mapping "multi-parameter" functions over functors, we get functors that contain functions inside them
a =fmap(+) [3, 5, 8]
-- not an example of applicative functors-- because it maps a normal function over the functor, instead of directly map with the functor's function
b =fmap (\f -> f 2) a
-- applicative functors
c = a <*> [2]
-- Applicative class preserves context (in this case, Just)
x =Just (*3) <*>Just5
monoids
-- monoid is when you have an associative binary function and a value which acts as an identity with respect to that function-- acts as an identity with respect to a function, it means that when called with that function and some other value, the result is always equal to that other value-- 1 is the identity with respect to * and [] is the identity with respect to ++.
mul1 x = x *1
concEmpty x = x ++[]
a = mul1 7
b = concEmpty [3, 5, 8]
typeclass Monoid
-- Monoid typeclass has a function named mappend-- it's a binary function that takes two values of the same type and returns a value of that type-- '<>' is the infix version of mappend
x = [1, 2] <> [3, 4]
monads
-- more robust applicative functor; Monad typeclass has '>>=' (called bind)-- instances of Monad: lists, Maybe, IO-- (>>=) :: (Monad m) => m a -> (a -> m b) -> m b -- if you have a value with a context (m a) how do you apply to it a function that takes a normal 'a' and returns a value with a context-- monadic value = values with context-- lists = >>= performs flat map
a = [3, 5] >>= (\x -> [x , x *2])
-- Maybe = passing values from one calculation to the next
b = (Just9) >>= (\x ->Just (x +1)) >>= (\x ->Just (x *2))
-- IO = >>= performs two actions sequentially
return
-- return is a function from Monad class-- it simply takes a value and puts it in a context (wrapped in a monad) that still holds that value-- in ghci: `:t (return 3)`-- Maybe is an instance of monada::MaybeInt
a =return7-- Just 7
b = (Just9) >>= (\x ->return (x +1)) -- return is used to preserve Maybe context
do
-- used to gluing together monadic values (e.g. IO) in sequence-- nested '>>='
a =Just3>>= (\x ->Just"!">>= (\y ->Just (show x ++ y))) -- Just "3!"-- do chainingb::MaybeString
b =do
x <-Just3
y <-Just"!"Just ((show x) ++ y)
in lists
-- >>= maps and flats a list
a = [3, 5, 8] >>= (\x -> [x, x *2])
-- nested
b = [1, 2] >>= (\n -> ['a', 'b'] >>=\ch ->return (n, ch))
-- do
c =do
n <- [1, 2]
ch <- ['a', 'b']
return (n, ch)
Examples
cashier program
importText.Printf
allBills = [100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05, 0.01] :: [Double]
-- convert values is needed because calculations with doubles/floats can become inaccurate over timedollar2Cent::Double->Int
dollar2Cent n =round (n *100)
cent2Dollar::Int->Double
cent2Dollar n = ((fromIntegral n) /100)
getChange:: [Double] ->Double-> [(String, Int)]
getChange _ 0=[]
getChange [] _ =[]
getChange bills change = tuple ++ (getChange (tail bills) (changeRest))
where
billValue =head bills
billsQuant =floor (change / billValue)
billValueString = printf "%.2f" billValue -- show float instead of scientific notation
tuple =if billsQuant >0then [(billValueString, billsQuant)] else[]
minus = ((fromIntegral (billsQuant)) * (dollar2Cent billValue))
changeRest = cent2Dollar ((dollar2Cent change) - minus)
printChange:: (Double, [(String, Int)]) ->IO()
printChange change = list >>= total -- monad operator is used to chain IO actionswhere
list =mapM_ (\tuple ->putStrLn (fst tuple ++" = "++ (show (snd tuple)))) (snd change)
total = (\_ -> (putStrLn ("Total = "++show (fst change))))
cashier::Double->IO()
cashier change = printChange result
where
bills =filter (<= change) allBills
result = (change, (getChange bills change))
main =do
cashier 90.94
cashier 70.96