Haskell reference

Haskell is a functional (that is, everything is done with function calls),
statically, implicitly typed (types are checked by the compiler, but you don't
have to declare them), lazy (nothing is done until it needs to be) language.

www.haskell.org/platform/
www.haskell.org/downloads/
www.haskell.org/hoogle/
For beginners - https://argumatronic.com/posts/1970-01-01-beginners.html

Documentation:
Haskell Documentation - https://haskell.org/
Haskell Tutorials - https://wiki.haskell.org/Tutorials
Haskell in 5 steps - https://wiki.haskell.org/Haskell_in_5_steps
Haskell Books and tutorials - https://wiki.haskell.org/Books_and_tutorials
Cabal User Guide - https://cabal.readthedocs.io/en/stable/
Haskell Cabal (Common Architecture for Building Applications and Libraries) -
https://www.haskell.org/cabal/index.html#nstall-upgrade
Basic libraries -
https://hackage.haskell.org/package/base-4.17.0.0/docs/Data-List.html
Math functions -
https://hackage.haskell.org/package/ClassyPrelude-0.1/docs/Prelude-Math.html
Collection of tools for numeric computations -
https://hackage.haskell.org/package/math-functions
Introduction to Haskell -
https://www.cis.upenn.edu/~cis1940/spring13/lectures.html
Haskell and Functional Programming -
https://courses.cs.washington.edu/courses/cse341/14wi/haskell/index.html
Glasgow Haskell Compiler -
https://downloads.haskell.org/ghc/latest/docs/users_guide/index.html
Learn you a haskell for great good - http://learnyouhaskell.com/
FP Complete Haskell - https://www.fpcomplete.com/haskell/learn/
Applied Haskell Syllabus - https://www.fpcomplete.com/haskell/syllabus/
Haskell for python developers -
https://www.softwarefactory-project.io/haskell-for-python-developers.html

Installation: https://www.haskell.org/ghcup/install/
$ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

System requirements [Linux Debian]
build-essential
curl
libffi-dev
libffi6
libgmp-dev
libgmp10
libncurses-dev
libncurses5
libtinfo5

ghcup user guide: https://www.haskell.org/ghcup/guide/
ghcup tui                   # text-based user interface
ghcup --help                # help
ghcup list                  # list available ghc/cabal versions
ghcup install ghc           # install the recommended GHC version
ghcup install ghc 8.2.2     # install a specific GHC version
ghcup set ghc 8.4.4         # set the currently "active" GHC version
ghcup install cabal         # install cabal install
ghcup upgrade               # update ghcup itself

ghc -- compiles Haskell libraries or applications to binary code
ghci -- an interpreter that lets you write Haskell code and get feedback

-- compile haskell program
$ ghc -o program program.hs

-- alternatively, we can skip the compilation phase
-- runghc interprets the source file instead of compiling it
$ runghc program.hs

-- -Wall flag will enable GHC to emit warnings about our code
$ ghc -Wall program.hs -fforce-recomp

-- build with ghc
$ ghc --make program.hs

-- essential commands
:?  -- list commands (show all commands)
:quit (or :q)
:load filename.hs (or :l filename.hs)
:reload -- reload the current file set after you'ved edited it (or :r)
:set editor name -- set editor to "name"
:edit name  -- edit current script
:cd -- change to another directory
:type expr -- give the type of an expression (or -- just :t)
:set +t tell Haskell to print type information after evaluation
:kind (or :k) -- kind inspection
:info (or :i) -- information
:print (or :p) -- print the expression
:module (or :m) -- add modules to imports
:add (or :ad) -- load a file into the REPL namespace
:instances (or :in) -- show instances of a typeclass
:browse (or :bro) -- browse all available symbols in the REPL namespace

-- The major namespaces are described below with their naming conventions
Namespace               Convention
Modules                 Uppercase
Functions               Lowercase
Variables               Lowercase
Type Variables          Lowercase
Datatypes               Uppercase
Constructors            Uppercase
Typeclasses             Uppercase
Synonyms                Uppercase
Type Families           Uppercase

-- the following list of keywords have a special meaning in the language, and
-- cannot be used as the names of functions or their arguments:
case        class       data        default     deriving
do          else        foreign     if          import      in
infix       infixl      infixr      instance    let
module      newtype     of          then        type        where

-- some basic types in Haskell
Type                        Name            Example value
Integers (limited range)    Int             42
Integers (infinite range)   Integer         7376541234
Reals                       Float           3.1415927
Booleans                    Bool            False
Characters                  Char            'a'
Strings                     String          "haskell"

-- some basic operations in Haskell
Name                Operation
a == b              Equality (a = b)
a /= b              Inequality (a != b)
a && b              Logical and (a ^ b)
a || b              Logical or (a v b)
logBase b x         Logarithm (log_b x)
truncate x          select integer part only

-- differences between function application in mathematics and in Haskell
Mathematics         Haskell
f(x)                f x
f(x, y)             f x y
f(g(x))             f (g x)
f(x, g(y))          f x (g y)
f(x) g(y)           f x * g y

-- single line comments are made with double dashes
{- multi-line comment made
    with braces and dashes
-}

-- executing the program in ghci
ghci> :l program.hs             (:load)
ghci> :r                        (:reload)

-- standard prelude
-- select the first element of a non-empty list
> head [1, 2, 3, 4, 5]
1
-- remove the first element from a non-empty list
> tail [1, 2, 3, 4, 5]
[2, 3, 4, 5]
-- select the nth element of list (counting from zero)
> [1, 2, 3, 4, 5] !! 2
3
-- select the first n elements of a list
> take 3 [1, 2, 3, 4, 5]
[1, 2, 3]
-- remove the first n elements from a list
> drop 3 [1, 2, 3, 4, 5]
[4, 5]
-- calculate the length of a list
> length [1, 2, 3, 4, 5]
5
-- calculate the sum of a list of numbers
> sum [1, 2, 3, 4, 5]
15
-- calculate the product of a list of numbers
> product [1, 2, 3, 4, 5]
120
-- append two lists
> [1, 2, 3] ++ [4, 5]
[1, 2, 3, 4, 5]
-- reverse a list
> reverse [1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]

-- defining functions
-- let functionName argName
ghci> let addOneTo i = i + 1   -- in this case the argument is i
ghci> addOneTo 5
6

-- factorial function
factorial :: Int -> Int
factorial n = if n == 0 then 1 else n * factorial(n - 1)

-- factorial computation without the above function
factorial n = product [1..n]

-- fibonacci function
fibonacci :: Int -> Int
fibonacci n = if n == 0 then 0 else if n == 1 then 1 else fibonacci(n - 2) + fibonacci(n - 1)

-- nested function using let
-- function that takes two numbers and outputs two numbers
alwaysEven a b = let isEven x = if even x
                                   then x
                                   else x - 1
                                in (isEven a, isEven b)

-- in can be written anywhere as long as it is written after the let functions

ghci> alwaysEven 30 23
(30,22)

ghci> isEven 5
<interactive> Not in scope 'isEven'

-- nested function using where
-- function that takes two numbers and outputs two numbers
alwaysEven a b = (isEven a, isEven b)
    where isEven x = if even x then x else x - 1


-- equality checks
ghci> 1 /= 2        -- in almost other programming languages 1 != 2
True
ghci> 1 == 1
True

-- function names
function123         -- correct
fUNCTION123         -- correct
function'           -- correct
Function            -- incorrect
123function         -- incorrect
'function           -- incorrect

-- infix functions: functions that take argument on the left and an argument on the right
a + b
a - b
a * b
a / b
a ^ b
a == b
a /= b

-- prefix functions: two arguments on the right
even a
max a b
min a b

-- infix functions can also be used as prefix functions when wraped in parentheses
(+) 1 2

-- prefix functions are also be used as infix functions
a `max` b
a `min` b

--lists and tuples are used to store multiple items
-- list: can store anything with single type (list of ints, list of characters)
same = ['l', 'c'] == "lc"

-- head: changes its type from list of int to int
head :: [a] -> a
head [1, 2, 3]
1
head "Institute"
'I'

-- tail
tail :: [a] -> [a]
tail [1, 2, 3]
[2, 3]
tail "Institute"
"nstitute"

-- last
last :: [a] -> a
last [1, 2, 3]
3
last "Institute"
'e'

-- init
init :: [a] -> [a]
init [1, 2, 3]
[1, 2]
init "Institute"
"Institut"

-- list of lists (nested list)
-- the nested list are seen as single values just like characters or ints in a list
let ourList = [[1, 2, 3], [4, 5, 6]]
ourList
[[1, 2, 3], [4, 5, 6]]
head ourList
[1,2,3]
tail ourList
[[4,5,6]]
last ourList
[4,5,6]
init ourList
[[1,2,3]]

-- get certain element from the list
[1, 2, 3, 4, 5, 6] !! 2
3  -- index starts from 0

-- add values to a list
: is an infix function that takes single value on its left and a list on its right
1 : [2, 3, 4, 5, 6]         -- which are of same type (prepending function)
[1,2,3,4,5,6]
++ is also an infix function which appends a list to another list which are of same type
[1, 2, 3] ++ [4, 5, 6]
[1,2,3,4,5,6]

-- the function to append list however is slower than the prepending function
-- this because when we prepend a value to a list it will be added almost instantly
-- however when we append a values the program has to go all the way to the end of the
-- list to append it and when dealing with large list this can become quite slow
-- haskell doesn't know where the end of the list is, it only knows what the first item is
-- so it has to check all the items before it can get to the end

-- laziness
-- simply put haskell only evaluates something if it is asked for, the cool thing about
-- that is when we need to get the first item of the list it wont bother with the rest of
-- it, it will evaluate the first item and nothing more, this is great in terms of
-- performance, and enable us to create interesting algorithms

ghci> ourList = [1, 2, 3, error "Whoops", 5, 6, 7]
ghci> ourList
[1,2,3,*** Exception: Whoops
ghci> take 3 ourList
[1,2,3]
ghci> drop 4 ourList
[5,6,7]
ghci> drop 3 ourList
[*** Exception: Whoops

-- this proves that whenever we ask for specific thing in haskell we truely only get that
-- and nothing more

-- range
ghci> [1..10]
[1,2,3,4,5,6,7,8,9,10]
ghci> [0,2..10]
[0,2,4,6,8,10]
ghci> [1..]         -- infinite list
ghci> take 3 (drop 5 [1..])
[6,7,8]

-- tuples
-- tuples are quite different from the lists and used for different purposes
-- they can carry multiple types in a single tuple whereas list can hold only one type

ghci> person = (22, "Kevin")        -- (id, "name")
-- all the functions that we learned for list cannot be applied for tuples
ghci> fst person
22
ghci> snd person
"Kevin"
-- these functions only work on the tuples of size 2
-- we can make tuples as large as 15 values but if we are creating a 15 value tuple then
-- the chances are there is better solutions to the problem

-- list of tuples
personList = [(22, "Kevin"), (35, "Zach"), (38, "Aiden")] -- database of users with id and name
ghci> lookup 35 personList
Just "Zach"
ghci> lookup 8 personList
Nothing

-- zip two list together to form a list of tuples
ghci> zip [1,2,3] ["yes","no","maybe"]
[(1,"yes"),(2,"no"),(3,"maybe")]
ghci> [1,2,3] `zip` ["yes","no","maybe"]
[(1,"yes"),(2,"no"),(3,"maybe")]

-- on a final note when we create a list of tuples every tuple must contain a same types
-- or else it wont work, differently constructed tuples are seen as different types
[(Int, String)] -- [(1, "yes"),(2, "no")]   - Correct
[(String, Int)] -- [("no", 2),("yes", 1)]   - Correct

-- calling functions is done by putting the arguments directly after the
function. There are no parenthesies as part of the function call:
> succ 5
6
> truncate 6.59
6
> round 6.59
7
> sqrt 2
1.4142135623730951
> not (5 < 3)
True
> gcd 21 14
7

-- I/O actions can be used to read from and write to the console.
> putStrLn  "The quick brown fox jumps over the lazy dog"
The quick brown fox jumps over the lazy dog
> putStr "No newline"
No newline>
> print (5 + 4)
9
> print (1 < 2)
True
-- The putStr and putStrLn functions output strings to the terminal. The print
function outputs any type of value. (If you print a string, it will have quotes
around it.)

-- If you need multiple I/O actions in one expression, you can use a do block.
Actions are separated by semicolons.
> do { putStr "2 + 2 =" ; print (2 + 2) }
2 + 2 = 4
> do { putStrLn "ABCDE" ; putStrLn "12345" }
ABCDE
12345

-- Reading can be done with getLine (which gives back a String) or readLn (which
gives back whatever type of value you want). The <- symbol is used to assign a
name to the result of an I/O action.
> do { n <- readLn ; print (n^2) }
4
16
(The 4 was input. The 16 was a result.)

-- There is actually another way to write do blocks. If you leave off the braces
and semicolons, then indentation becomes significant. This doesn't work so well
in ghci, but try putting the file in a source file (say, program.hs) and build
it.

main = do putStrLn "What is 2 + 2?"
        x <- readLn
        if x == 4
            then putStrLn "You're right!"
            else putStrLn "You're wrong!"

-- You can build with ghc --make program.hs, and the result will be called
program.














-- Haskell Tutorial
-- An Introduction to Haskell from a non-programmer
-- DistroTube

-- Single line comments are made with double dashes

{- Multi-line comment made with braces and dashes.
 - Here is the second.
 - And a third !
-}


-- Haskell is a functional programming language.
-- Everything is immutable so you cannot change it!
-- Haskell is lazy which means it only executes something when it needs to.

-- Documentation:
-- http://learnyouahaskell.com/
-- https://haskell.org

-- :l file.hs (to load file.hs in ghci)
-- :r reload  (to reload file.hs in ghci)
-- :t variabelName (to find the type of variableName)
-- :!clear  (to run built-in shell command from ghci)
-- :q   (to quit the ghci)

-- Imports
import Data.List

-- There are five numeric types in the Haskell "prelude" (the part of the
library you get without having to import anything):
Int         is an integer with at least 30 bits of precision
Integer     is an integer with unlimited precision
Float       is a single precision floating point number
Double      is a double precision floating point number
Rational    is a fraction type, with no rounding error
-- All five are instances of the Num type class. The first two are instances of
Integral, and the last three are instances of Fractional.

-- Types
-- Bool, Int, Integer, Float, Double, Char, [Char], Tuples ()

-- :: Bool
-- True or False
trueAndFalse = True && False
trueOrFalse = True || False
notTrue = not(True)
boolFive = 5 > 4

-- :: Int
-- Whole number: from -2^63 to 2^63
maxInt = maxBound :: Int
minInt = minBound :: Int

-- :: Integer
-- Unbounded whole number
numFive :: Integer
numFive = 5

-- :: Float
numFive' = 5.0 :: Float
myFloat :: Float
myFloat = 1.0 + 2.5

-- :: Double
-- default floating point number in Haskell is Double
-- eleven point precision
myFloat' = 1.0 + 2.5
myDouble = 1.55555555555 + 0.00000000001

-- :: Char
-- Char are single characters and denoted within single quotes
singleChar = 'a'
myName :: [Char]
myName = "Saran"
myName' = ['S', 'a', 'r', 'a', 'n'] -- same as the above

-- Math
addNum = 3 + 6
subNum = 3 - 6
mulNum = 3 * 6
divNum = 3 / 6
modNum = mod 9 2        -- prefix operator
modNum' = 9 `mod` 2     -- infix operator
addNeg = 4 + (-4)

-- pi, exp, log, sin, cos, tan, asin, acos, atan

truncNumber = truncate myDouble -- truncating the number 1.55555555556 to 1
roundNumber = round myDouble    -- rounding the number 1.55555555556 to 2
ceilNumber' = ceiling myDouble  -- ceiling the number 1.55555555556 to 2
floorNumber = floor myDouble    -- flooring the number 1.55555555556 to 1

-- sqrt :: Floating a => a -> a
sqrtFive = sqrt numFive'

-- Lists
numList = [1,2,3,4,5]
numList' = [1..5]   -- range of numbers
alphaList = ['a'..'z']
evenNum = [2,4..20]
oddNum' = [1,3..20]
oddAlpha' = ['a','c'..'z']
evenAlpha = ['b','d'..'z']
sumNumList = sum [1..5]
sumNumList' = sum numList
prodNumList = product [1..5]
prodNumList' = product numList
elemList = elem 5 numList   -- is element 5 part of numList
elemList' = 5 `elem` numList
fibNum1 = [0,1,1,2,3,5,8]
fibNum2 = [13,21,34,55,89,144]
fibNum' = fibNum1 ++ fibNum2
maxFib = maximum fibNum'
minFib = minimum fibNum'
listOfList = [fibNum1, fibNum2]
addListNum = zipWith (+) [1,2,3,4,5] [6,7,8,9,10]
addFibList = zipWith (+) fibNum1 fibNum2
infiniteNumList = [1,2..]
notInfinite = take 20 infiniteNumList
infiniteFive = repeat 5
notInfiniteFive = take 20 infiniteFive
repeatFive = replicate 20 5     -- replicate 5 for 20 times
cycleFive = cycle [5]
cycleFibn = cycle fibNum'
takeCycle = take 50 (cycle [1,2,3,4,5,6,7,8,9,10])  -- with parentheses
takeCycle' = take 50 $ cycle [1,2,3,4,5,6,7,8,9,10] -- same as the above
dropFibn = drop 5 fibNum'   -- drop the first 5 of the list fibNum'
filterFibn = filter (>5) fibNum' -- print greater than 5 values
whileFibn = takeWhile (<=89) fibNum' -- print lesser than or equal to 89 values
mapList = map (*2) [1..10]  -- multiply 2 with numbers 1 to 10
unordList = [545,2,34,87,3,897,56,13]   -- some unordered list
sortList = sort unordList   -- sort the unordered list
firstNum = head fibNum'     -- print the first value in the list
restNum' = tail fibNum'     -- print the values in the list but the first value
lastNum' = last fibNum'     -- print the last value in the list
initNum' = init fibNum'     -- print the values in the list but the last value
prodList = foldr (*) 1 [2,3,4,5] -- same as 2 * (3 * (4 * (5 * 1)))
subrList = foldl (-) 1 [2,3,4,5] -- same as (((((1) - 5) - 4) - 3) - 2)
summList = foldl (+) 0 [1..100]  -- same as sum [1..100]
appendList = 1 : []
appendList' = 1 : 2 : 3 : 4 : 5 : []

-- Fibonacci Numbers generation
fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)
-- [0,1] ([1]) -> [0,1,1,2] ([1,1,2]) -> ...

-- Functions
listFunction = [x * y | x <- [1..5], y <- [1..5]]
listFunction' = [x * y | x <- [1..5], y <- [1..5], x * y `mod` 3 == 0]

-- Tuples
tuple' = ("Raman", 18)
getName = fst tuple'
getNumm = snd tuple'

empName = ["John Doe", "Jane Doe", "Mary Jane", "Ben Dover"]
empNumm = [1001, 1002, 1003, 1004]
empList = zip empName empNumm

-- simple functions
summ a b = a + b
prod a b = a * b
func a b c = summ (prod a b) c


-- program
-- to compile the program: ghc program.hs
-- to run inside ghci: main

greeting = "Hello, World!"

--main = do
--    print greeting


-- program to take input
--main = do
--    putStrLn "Enter name: "
--    name <- getLine
--    putStrLn ("Hello, " ++ name ++ "!")


-- program with function
-- Haskell is picky about spacing!
lowerCase :: [Char] -> [Char]
lowerCase s = [c | c <- s, elem c ['a'..'z']]

main = do
    let s = lowerCase "Institute of Mathematical Sciences"
    print(s)


-- list
: is an infix function that takes single value on its left and a list on its right
1 : [2, 3, 4, 5, 6]         -- which are of same type (prepending function)
[1,2,3,4,5,6]
++ is also an infix function which appends a list to another list which are of same type
[1, 2, 3] ++ [4, 5, 6]
[1,2,3,4,5,6]

-- quick sorting algorithm function
f [] = []
f (x:xs) = f ys ++ [x] ++ f zs
            where
               ys = [a | a <- xs, a <= x]
               zs = [b | b <- xs, b > x]

-- x = 3 and xs = [1, 4, 2]
f [3, 1, 4, 2] = f (3:[1, 4, 2]) = f [1, 2] ++ [3] ++ f[4]
                                 = f [] ++ [1] ++ f[2] ++ [3] ++ [4]
                                 = [] ++ [1] ++ [2] ++ [3] ++ [4]
                                 = [1, 2, 3, 4]

-- type signatures
Functions map input values to output values, for example, inc maps integers to
integers. Thus we denote the type of inc as Int -> Int.
inc :: Int -> Int               -- type signature
inc x = x + 1                   -- function equation

-- multiple arguments
average :: Float -> Float -> Float
average a b = (a + b) / 2.0

average 3.0 4.0 = (3.0 + 4.0) / 2.0 = 3.5
