26 set 2024
Haskell notes (part 01)
- These are few notes on my Haskell study…
What is Haskell?
- Haskell is a functional language
- Meaning that its paradigm is not OO (Object Oriented), neither Imperative
- A functional programming language uses a basic method of aplying functions to its arguments (or parameters)
- There ara a ton of good reasons why you should learn a functional language like Haskell
- Suffice to say that most OO and Imperative languages borrow methods and concepts from functional ones…
- But, you can do your own research on this subject…
What is a Haskell function? (01)
- It is an entity that takes a value or a set of values (including other functions)
- And returns (reduces it to, evaluate it to, execute it to) a new value ;)
- You should first define a function, and then, execute (call, evaluate) it
- You can define a function in a script, using an code editor
- As well as in GHCI, but you need to write the definition and its body on a single line separated by “;” (a semi-colon)
What is a Haskell function? (02)
- e.g.:
- Int -> Int -> Int ; soma x y = x + y
- or
- Float -> Float -> Float ; soma x y = x + y
- or
- Double -> Double -> Double ; soma x y = x + y
- Call it like so: soma 2 3 – (et.al)
- :t – check their types
Translating mathematics functions into Haskell ones
- Note that function applications default from left to right
- 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
- et.al.
The Haskell platform (01)
- Avoid Window$ OS for working at all costs…
- Especially when learning coding/programming…
- Avoid VsCode (and VsCodium) at all costs as well…
- They are a declaration of lazy programmers (in an extremely bad sense (note in the good sense of coding))
- Worst of all, if they are used on a Linux environment, it is an heresy and a profanation ate the same time… ;)
- Unless, they impose it on you when working locally and/or remotely
The Haskell platform (02)
- Preferably, use a GNU/Linux distro like Debian…
- and Emacs (or an Emacs start-kit) and/or Vim (or a Vim start-kit)
- These are real editors for serious programmers ;)
- If you do not have the muscle brain to use one of them or both, Zed can be berable as your code editor:
- https://zed.dev/
- Installing haskell on Linux Debian (or Debian-like) OSes
- sudo apt install haskell-platform
- sudo apt autoremove
Haskell Programming Language
- A simple “Hello Haskell!” script
- Copy this code into a, say, “hello_hask.hs”
- And read on to learn how to compile/run it…
import Data.Binary.Put (putShortByteString) import Data.Text.Encoding.Error (ignore) main = do putStrLn "Hello! World!" -- Just ignore this part, if you do not use Emacs and eev -- (buffer-file-name) {- (eepitch-ghci) (eepitch-kill) (eepitch-ghci) :load hello.hs -}
- How do you start a ghci REPL in Linux?
- Just type “ghci” in a terminal
- Then you can “:set prompt ”$sth“” as you wish
- :set prompt "hsk > "
- You can call “M-x helm-ucs” and choose “𝜆 > ”
- as your prompt, e.g. :)
The haskell interpreter
- ghci is the haskell interpreter
- You can also run a program callint it on a terminial
- followed bye the name of the haskell script. Like so:
- ghci foo.hs
- ghci /path/to/script.hs
Then you can start issuing commands at the REPL..
- putStrLn "Hello Haskell!"
For getting help and information, do:
- :doc $func
- :help $func
- :info $func
- local help:
- dino laptop
- (browse-url-firefox “~/.ghcup/ghc/9.4.8/share/doc/ghc-9.4.8/html/index.html”)
- cypher laptop
- (browse-url-firefox “~/.ghcup/ghc/9.6.1/share/doc/ghc-9.6.1/html/index.html”)
- On-line help:
- ghc-docs
- (browse-url-firefox “https://downloads.haskell.org/ghc/latest/docs”)
- hoogle-docs
- (browse-url-firefox https://hoogle.haskell.org")
- On Emacs (a personal local function):
- M-x cypher/haskell-docs
For repeating the last command
- Type “it” on GHCI
For loading, reloading and calling a function from your prompt, do:
- :main
- :l(oad)
- :l /path/to/script.hs
- :r(eload)
- :r /path/to/script.hs
- :q(uit) or “C-d”
- setting your Haskell editor
- ghci> :edit hello.hs
- editor not set, use :set editor
- ghci> :set editor emacsclient
For running a program on console, do:
- runhaskell $script.hs
For compiling a script, do:
- ghc $script.hs
Then you can run your compiled code like so:
- ./$script
For issuing a bash command inside de REPL, do:
- :!$cmd
- $cmd is just a variable
Start writing your haskell scripts with: (01)
- This is just an example: - module $Test where - main :: IO() -- it is the monad IO () - the Input/Output monad - main = putStrLn "Hello Haskell!" - If you have multiple lines after "main =", you've to write: - main = do - and open a new line (2 spaces of left-indentation) - for writing your code...
Start writing your haskell scripts with: (02)
- What is a module?
- module is a place where we keep all functions we’ve defined
- Prelude is the default Haskell module where all standard functions are already defined
- Sometimes, we have to import a module for building more specific applications…
Commenting your Haskell code
- Commenting your code is a good programming practise
- It helps you, and others, who reads the code
- Especially, if you visit it after a while…
- It serves as a way of documenting your code as well
- For commenting single lines, use two dashes “–” before or after them
- Like so: – this is a single-line comment
- Multi-line comments start with curl-braces and a single dash and close the with single dash and curl-braces.
- Like so: {- Write lines here -}
- Multi-line comments can be nested
Haskell classes and types
- Note Haskell is case-sensitive
- What is a type?
- It is set of related values. E.g.: Numbers (Int, Integers, Double and Float)
- Boolean types hold logic values: True and False
- We can combine types and build functions
- Boolean types map all functions from a Bool to another Bool
- Bool -> Bool
- :t (&&) - yields True if the two sides are True
- :t (||) - inclusive Or - yields False if the two are False, otherwise renders True
- :t not - it changes the Boolean values to be the opposite of themselves
- Haskell has got a short-circuit semantic
- We can call it a lazy evaluation
- False && True && True – this yields False, because it stops after evaluating the two first elements
- T~ (v has got a type T)
- We have to assing a type to every expression we intend to execute (evaluate)
- Example of expressions: 1; not True; not (not False); 3 + 4; et.al.
- :t (+)
- :t map
- :t filter
- Int~) + 2
- Float~) + 2
- Bool~ (3 has got a type Bool) gives an error
- Types should be coherent, otherwise the interpreter will yell at them
- Before evaluating any expression, Haskell checks its type on the background
- This checking up is called type inference
- if True 1 ; else False – this gives an error. Because True is an Bool and has to return a Bool and not an Int
You can check types with:
- :t(ype) $func ($id)
- for operators, you’ve to place them inside parenthesis, so that it will be possible to see their types:
- :t (*) – you’ve to enclose infix operators with parentheses to turn them into prefix ones for checking their types
- :t (==) – you’ve to enclose infix operators with parentheses to turn them into prefix ones for checking their types
- An script to show some math operators and math functions in action
module Main where main :: IO () main = do print (5 + 5) print (5 * 7) print (8 - 3) print (5 / 2) print (3 * 6.0) print (4 ** 3) print (sqrt 36.0) print (round 4.4) print (round 4.6) print (floor 4.6) print (ceiling 4.6) return () -- Just ignore this part, if you do not use Emacs and eev -- or type "C-c C-l". So that, the function is automagically loaded into haskell REPL -- M-x haskell-doc-mode -- (buffer-file-name) {- (eepitch-ghci) (eepitch-kill) (eepitch-ghci) :load maths.hs :main -}
Bool and compare
- ’a’ == ’b’ -> False
- ’a’ `compare` ’b’ -> LT
The “show” type class prints values as strings
- :t show 3 -> show 3 :: String
The “read” type class prints values as non-strings
- :t read "3" -> read "3" :: Read a => a - read "3" + 5 - read "True" && True - read "4" -- error, since it needs a type annotation, because it - does not know if you want a Float or Int - read "4" :: Int - read "4" :: Double - read "5" :: Float - (read "4" :: Float) * 3 - read "[1,2,2]" :: [Float] - read "('a', 3.4)" :: (Char, Float)
- A simple doubleMe and doubleUsSumUs haskell functions
- As usual, code the code; save it on a “.hs” ending file and compile/run it
-- load the function in the repl with: -- :l hello -- after saving it, you do not need to load again, but: -- :reload -- and call it with: -- :main -- Script from the book: -- Learn You a Great Haskell -- double an only given number doubleMe x = x + x -- double two given numbers -- and sum them doubleUsSumUs x y = (x * 2) + (y * 2) -- Just ignore this part, if you do not use Emacs and eev -- or type "C-c C-l". So that, the function is automagically loaded into haskell REPL -- M-x haskell-doc-mode -- (buffer-file-name) {- (eepitch-ghci) (eepitch-kill) (eepitch-ghci) :load baby.hs -}
Compiler type inference
- the compiler can infer what we want, - by looking to the rest of the - list elements - [read "True", False, True, False]
You can get a range with “..” notation inside a list
- [1..10] – [start..end]
- [LT .. GT]
Checking bounded types
- minBound :: Int - maxBound :: Int - maxBound :: (Bool, Int, Char)
Functions (built-in)
- e.g.:
- 4 + 2
- 4 - 2
- 4 * 2
- 4 ^ 2
- 4 ** 2
- 4 / 2
- 4 `div` 2
- 4 / 0
- 4 `div` 0
- succ ’a’
- succ 7
- sum [1..5]
- et.al.
Function naming (rules)
- No space between two or mor strings
- Must start with downcase letter
- Can have down, upper-cases letters, digits, underscores, and single quote in its end
- camelCase style
- e.g.: fooBar; foo1; foo’; foo’’; foo’’’ (’’’ …)
module Dup where -- You can write functions with same names (with two (or more) -- versions (you can use prime(s) "'" for that), and different types -- load and call the functions on GHCI -- dupFoo 3 -- dupFoo' 3 -- function declaration dupFoo :: Int -> Int -- build the function dupFoo n = n + n -- function declaration dupFoo' :: Float -> Float -- build the function dupFoo' n = 2 * n {- (eepitch-ghci) (eepitch-kill) (eepitch-ghci) :load dup.hs -}
- Cannot use Haskell reserved words: case, class, data default, deriving, do, else, if, import, in, infix, infixl, infixr, instance, let, module, newtype, of, then, type, where
Function assignments
- e.g.:
- What does the following expression mean?
- div :: Integral a => a -> a -> a - div :: Int -> Int -> Int
- It means that the div(ision) function takes two arguments (or parameters) of type Int and returns a value of the same type (Int)
- Integral is a Class, in Haskell
- division x y = x `div` y
- or
- division x y = div x y
- It is not necessary to add function signatures in your heading code, but Haskell good programming practice say so…
Functions can be classified on:
- prefixed functions
- e.g.: - 2
- div 4 2
- infix functions
- 2 + 4
- 4 `div` 2
- post-fixed functions
- 2 sqrt
Assigning non-default types to data
- :t 20 - 20 :: Int - 20 :: Float - 20 :: Double
fromIntegral function
- :t fromIntegral
- (length [1,2,3,4]) + 3.0 – error
- fromIntegral (length [1,2,3,4]) + 3.7
Lists are the most common types Haskell uses to manipulate data
- There are no loops on Haskell
- It uses recursive functions (functions that call themselves)
- Which are most powerful methods to reach what loops do on OO and Imperative languages
List names (convention)
- ns – list of numbers
- xs – list of arbitrary values
- xss – list of list of characters
Joining data with “<>” (used to be “++”)
- e.g.: - lists: - [1,2,3] <> [4,5,6] - lists of lists - [[1,2,3],[4,5,6]] <> [[7,8,9]] - strings (chars, for real): - ['h', 'e'] <> "llo"
Accessing data elements with “!!”
- words = ["foo", "bar", "barz", "foo1", "bar2", "barz1", "barz2"] - words !! 0 - words !! 5 - words !! 10
- An script to show how to acces list elements with:
- !!
- head
- last
- init
- And
- tail
main :: IO () -- Lists start at 0 index scores :: [Int] scores = [5, 7, 9, 10] -- the bang operator access list indexes main = do print [scores !! 0] print [scores !! 2] print (head scores) print (last scores) print (init scores) print (tail scores) -- Just ignore this part, if you do not use Emacs and eev -- or type "C-c C-l". So that, the function is automagically loaded into haskell REPL -- M-x haskell-doc-mode -- (buffer-file-name) {- (eepitch-ghci) (eepitch-kill) (eepitch-ghci) :load lists.hs :main -}
Consing (constructing) lists with “:”
- 1 : [2,3] -- prepends 1 to the list, using cons ":" - 'h' : ['e', 'l', 'l', 'o'] - 'f' : 'o' : ['o'] - [1,2,3] : [] - [1] : [2] : [3] : [[10,11,12]] - l = [1] : [2] : [3] : [[10,11,12]]
Accessing list elements with head, init and tail
- l = [1] : [2] : [3] : [[10,11,12]] - head l - init l - tail l
Data pattern matching
- Copy the function bellow and save it as:
- e.g.: patMatch.hs
module PatMatch where -- partern matching sorte :: Int -> String sorte 7 = "Acertou! Parabéns" -- load this function and -- call this it like so: -- sorte # -- where "#" is a number -- we should always add a catchall at the end of our patern matching function sorte x = "Que pena... Errou" -- ignore this part if you do not have "eev" in Emacs {- (eepitch-ghci) (eepitch-kill) (eepitch-ghci) :load partern_match.hs -}
Emacs Haskell Niceties (1)
- Type "C-c C-l" when editing a haskell script. So that, the function is automagically loaded into haskell REPL - (buffer-file-name) - eval this inside your local buffer or file
Emacs Haskell Niceties (2)
- You can use eeit - and just add ":main" to its block - or you can call ghci with "C-c C-l" as well - use 2 spaces indentation - M-x haskell-doc-mode
Branches and decision making
- if, then, else
- A simple script
module Main where -- call the down function main = printSmallNumber 11 -- function name = body printSmallNumber num = if num < 10 then print num else print "The number is too big"
Branches and decision making (cont…)
-- Just ignore this part, if you do not use Emacs and eev -- or type "C-c C-l". So that, the function is automagically loaded into haskell REPL -- M-x haskell-doc-mode -- (buffer-file-name) {- (eepitch-ghci) (eepitch-kill) (eepitch-ghci) :load prNum.hs -}
Good Haskell tutorials:
- Books - Effective_Haskell - Learn_You_a_Great_Haskell - Haskell_98_Tutorial - Haskell_TCofP - Haskell_PaTyp - Introduction au langage Haskell - by nokomprendo - gitlab - http://tny.im/CxTon - Articles - http://tny.im/qZbsp - Videos - http://tny.im/lFIfd