Haskell
Introduction
This is my first dip into Haskell, just felt like trying it. Haskell is a purely functional programming language. rather than a declarative language. I.E. it is a declarative language rather than imperative. With declarative, you tell the language what the thing is rather than how to do the thing.
Features
Lazy
Haskell is a programming lazy language and only evaluates on usage. E.g. only evaluates the first 10
let infiniteList = [1..]
take 10 infiniteList
Modules
Starting to look a lot like Lisp. This next bit shows how you can combine various functions of your own to produce and output
let double = x = x * 2
let increment x = x + 1
let doubleThenIncrement x = increment(double x)
doubleThenIncrement 10 -- Answer is 21
Statically Typed
With Haskell types are inferred
Hello World
Here we start with main and strange format but no curly brackets which is good.
main :: IO()
main = putStrLn "Hello World"
We build like gcc using
ghc hello.hs -o hello
Date Models
Types
- Numbers e.g. 1
- Chars e.g. "S"
- Boolean e.g. True, False
- List e.g. [1,2,3], [True, False, True]
- Tuple e.g. (1,2,3, "string, True)
List Comprehension
You can define a function and operate against a list like this
[x*2 | x <-[1..10]] -- Answer is [2,4,6,8,10,12,14,16,18,20]
Constructs
Pattern Matching
No surprises, and I love pattern matching
-- Pattern Matching
coffeeType :: String -> String
coffeeType "Epresso" = "Strong and bold"
coffeeType "Cappuccino" = "Frothy and delicious"
coffeeType "Filter" = "Watery and weak"
coffeeType _ = "I have no idea what that is"
main = do
print(coffeeType "Epresso")
print(coffeeType "Cappuccino")
print(coffeeType "Filter")
print(coffeeType "Latte")
Guards
Again no surprise here
bookCategory :: Int -> String
bookCategory age
| age < 10 = "Children's book"
| age < 18 = "Teen book"
| otherwise = "Adult book"
main = do
print(bookCategory 5)
print(bookCategory 15)
print(bookCategory 25)
Where Clause
Almost writes its self
populationDensity :: (Double, Float) -> String
populationDensity (population, area) = density where density = population / area
main = do
print(populationDensity (100000, 1000))
Recursion
Where would we be without Factorial
-- Factorial
factorial :: Int -> Int
-- Base case
factorial 0 = 1
-- Recursive case
factorial n = n * factorial (n - 1)
main = do
print(factorial 5)
Higher Order Functions
Higher order functions are functions that take one or more functions as arguments, or return a function as their result. In the example we had our friends from Typescript, map and filter,
let increment x = x + 1
map increment [1,2,3] -- [2,3,4]
And filter, loving the mod operation
let isEven n = n `mod` 2 == 0
filter isEven [2,3,4] -- [2,4]
Finally something new, foldl, i.e. apply operation start from position using array from left to right (lispy)
foldl (+) 0 [1,2,3,4,5] -- 15
And now first use of libraries
import Data.List (sort)
sort [10, 2, 5, 3, 6, 7, 4, 8, 9, 1]
Other functions were zipWith, takeWhile, dropWhile
-- All
all even [1,2,4] -- False
any even [1,2,4] -- True
Lambda Expressions
This is a shortcut to writing unnamed function. We do this by specifying a back slash. e.g.
print((\c -> c * 9/5 + 32) 25) -- 77.0
More On Functions
Other Built-in Functions
We have head,tail, last, init
let x = [1..10]
head x -- 1
tail x -- [2,3,4,5,6,7,8,9,10]
last x -- 10
init x -- [1,2,3,4,5,6,7,8,9]
reverse x -- [10,9,8,7,6,5,4,3,2,1]
length x -- 10
take 5 (x) -- [1,2,3,4,5]
drop 6 (x) -- [7,8,9,10]
sum x -- 55
product x -- 3628800
elem 8(x) -- True Searches list
Function Composition
Here we can combine functions in one call. This just looks like chaining to me but here is the example like in Maths when you have f{g(x)}
-- IsEven function
isEven :: Int -> Bool
-- Is Odd function
isNotEven :: Bool -> String
isEven x = if x `rem` 2 == 0
then True
else False
isNotEven x = if x == True
then "This is an Even Number"
else "This is an Odd Number"
main = do
print((isNotEven.isEven) 5)
Modules
Modules are, as ever, just functionality you can import. We looked at Data.List
main = do
print(intersperse '|' "ILoveHaskell") -- I|L|o|v|e|H|a|s|k|e|l|l
print(intercalate ' ' ["Haskell", "is", "great"]) -- "Haskell is great"
print(splitAt 3 "Apple") -- ("App","le")
And the Data.Char module.
main = do
print(toUpper 'a') -- A
print(toLower 'B' -- b
print(words 'FRED WAS') -- ["FRED", "WAS"]
And the Data.Map. The importing of this seemed to be a bit blurry even to the tutor
import Data.Map (Map)
import qualified Data.Map as Map
myMap :: Integer -> Map Integer [Integer ]
myMap n = Map.fromList (map makePair [1..n])
where makePair x = (x, [x])
main = do
print $ myMap 10
And the Data.Set
main = do
print $ Set.fromList "Hey Jude!" -- " !HJdeuy"
Roll your Own
This seem a little hairy to recompile and make sure we are running the correct code. Possibly need to look at the tools for this
module Custom(
showEven,
showOdd
) where
showEven :: Int -> String
showEven x = if x `mod` 2 == 0 then "Even" else "Odd"
showOdd :: Int -> String
showOdd x = if x `mod` 2 == 0 then "Odd" else "Even"
File IO
Reading
Very easy
main = do
contents <- readFile "movies.txt"
putStr contents
Writing
main = do
let movies = ["The Godfather", "The Shawshank Redemption", "The Dark Knight", "Pulp Fiction", "The Lord of the Rings: The Return of the King"]
writeFile "movies.txt" (unlines movies)
putStrLn "Movies written to movies.txt"
Exception Handling
Here is an example of catching a division by zero in Haskell
import Control.Exception
main = do
result <- try (evaluate (div 5 0)) :: IO (Either SomeException Int)
case result of
Left ex -> putStrLn $ "Caught exception: " ++ show ex
Right val -> putStrLn $ "The result was: " ++ show val
Functors
Introduction
The definition I got from C++ is function object is a function with state.
Simple C++ Example
struct Value {
int m_result {0};
int operator()(int newResult) {
m_result = newResult;
return m_result;
}
}
int main() {
Value v;
v(42);
std::cout << v.m_result << std::endl;
}
Better C++ Example
They went on to provide a second C++ example where the Comparator was used as a good use of a functor. Calling the function Comparator with the state of the goblins.
struct Goblin {
int health;
int strength;
Goblin(int health, int strength) : health(health), strength(strength) {};
bool operator<(const Goblin& other) const {
return health < other.health;
}
};
struct GoblinComparator {
bool operator()(const Goblin& a, const Goblin& b) const {
return a.strength < b.strength;
}
};
int main() {
std::vector<Goblin> goblins {
{5, 25},
{3, 25},
{100, 1}
};
std::sort(goblins.begin(), goblins.end(), GoblinComparator());
for(const auto& goblin : goblins) {
std::cout << "Health: " << goblin.health << " Strength: " << goblin.strength << std::endl;
}
return 0;
}
// Health: 100 Strength: 1
// Health: 5 Strength: 25
// Health: 3 Strength: 25
fmap
For lists fmap and map work the same way
main = do
print(map (subtract 1 ) [1,2,3]) -- [0,1,2]
print(fmap (subtract 1 ) [1,2,3]) -- [0,1,2]
Monads
So this is the Monad bit which I what I started on to learn this. Like Functors, the explanation may have been written in greek. I have no idea what he meant
class Monad m where
return :: a -> m a
-- Bind
(>>=) :: m a -> (a -> m b) -> m b
-- Then
(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y
-- Fail
fail :: String => m a
fail msg = error msg
He preceded to speak about 3 laws of Monads
--- Three Laws of Monads:
--- 1 Left Identity:
return a >> = f
--- the same as : f a
-- 2 Right identity:
x >>= return
-- the same as: x {Value not changed}
-- 3 Associativity:
(m >>= f) >>= g
m >>= (\x -> f x >>=g)
Moniods
The explanation was again useable. This is left here to fill in later