Haskell is a purely functional programming language. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. You cannot set a variable to something and then set it to something else later. The only thing a function can do is calculate something and return it as a result. It can have some very nice e consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result, which is called referential transparency.
Haskell is lazy.That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to showyou a result.
Haskell is statically typed. That means that a lot of possible errors are caught at compile time.Haskell uses a very good type system that has type inference.That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. Type inference also allows your code to be more general.
Haskell is elegant and concise. Because it uses a lot of high level concepts, Haskell programs are usually shorter than their imperative equivalents.
A text editor and a Haskell compiler. GHC can take a Haskell script (they usually have a .hs extension) and compile it but it also has an interactive mode which allows you to interactively interact with scripts.You can call functions from scripts that you load and the results are displayed immediately. The interactive mode is invoked by typing in ghci at your prompt.
The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In Haskell every expression and function must return something. Another thing about the if statement in Haskell is that it is an expression.
doubleSmallNumber' x = (if x > 100 then x else x*2) + 1That apostrophe doesn’t have any special meaning in Haskell’s syntax. It’s a valid character to use in a function name. We usually use ’ to either denote a strict version of a function (one that isn’t lazy) or a slightly modified version of a function or a variable.
Note:
Functions can’t begin with uppercase lettersWhen a function doesn’t take any parameters, we usually say it’s a definition (or a name) conanO'Brien = "It's a-me, Conan O'Brien!"Lists are denoted by square brackets and the values in the lists are separated by commas. Speaking of characters, strings are just lists of characters. “hello” is just syntactic sugar for [‘h’,‘e’,‘l’,‘l’,‘o’] . Because strings are lists, we can use list functions on them, which is really handy.
A common task is putting two lists together. This is done by using the ++ operator.
ghci> [1,2,3,4] ++ [9,10,11,12] [1,2,3,4,9,10,11,12] ghci> "hello" ++ " " ++ "world" "hello world" ghci> ['w','o'] ++ ['o','t'] "woot"++ operator can take a long time when dealing with long lists. Putting something at the beginning of a list using the : operator (also called the cons operator) is instantaneous.
ghci> 'A':" SMALL CAT" "A SMALL CAT" ghci> 5:[1,2,3,4,5] [5,1,2,3,4,5]Notice how : takes a number and a list of numbers or a character and a list of characters, whereas ++ takes two lists.
If you want to get an element out of a list by index, use !! . The indices start at 0.
ghci> "Steve Buscemi" !! 6 'B' ghci> [9.4,33.2,96.2,11.2,23.25] !! 1 33.2Lists can also contain lists. They can also contain lists that contain lists that contain lists … The lists within a list can be of different lengths but they can’t be of different types. Lists can be compared if the stuff they contain can be compared. When using < , <= , > and >= to compare lists, they are compared in lexicographical order.
head takes a list and returns its head. The head of a list is basically its first element. tail takes a list and returns its tail. In other words, it chops off a list’s head. last takes a list and returns its last element. init takes a list and returns everything except its last element.
length takes a list and returns its length.
null checks if a list is empty. If it is, it returns True , otherwise it returns False . Use this function instead of xs == [] (if you have a list called xs )
reverse reverses a list. -
take takes number and a list. It extracts that manyelements from the beginning of the list.
drop works in a similar way, only it drops the number of elements from the beginning of a list.
maximum takes a list of stuff that can be put in some kind of order and returns the biggest element.
minimum returns the smallest.
sum takes a list ofnumbers and returns their sum.
product takes a list of numbers and returns their product.
elem takes a thing and a list ofthings and tells us if that thing is an element ofthe list. It’s usually called as an infix function because it’s easier to read that way.
Ranges are a way of making lists that are arithmetic sequences of elements that can be enumerated. Numbers and characters can be enumerated. What if we want all even numbers between 1 and 20? Or every third number between 1 and 20? To make a list with all the numbers from 20 to 1, you can’t just do [20…1] , you have to do [20,19…1] . Note: Not to use floating point numbers in ranges! They are not completely precise! You can also use ranges to make infinite lists by just not specifying an upper limit.
List comprehensions are very similar to set comprehensions. The list comprehension we could use is [x*2 | x <- [1…10]] . x is drawn from [1…10] and for every element in [1…10] (which we have bound to x ), we get that element, only doubled. We can add a condition (or a predicate) to that comprehension. Predicates go after the binding parts and are separated from them by a comma. Note that weeding out lists by predicates is also called filtering .
Not only can we have multiple predicates in list comprehensions (an element must satisfy all the predicates to be included in the resulting list), we can also draw from several lists. When drawing from several lists, comprehensions produce all combinations of the given lists and then join them by the output function we supply.
ghci> [ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50] [55,80,100,110]Our own version of length
length' xs = sum [1 | _ <- xs]_ means that we don’t care what we’ll draw from the list anyway so instead of writing a variable name that we’ll never use, we just write _. This function replaces every element of a list with 1 and then sums that up. This means that the resulting sum will be the length of our list.
Note: because strings are lists, we can use list comprehensions to process and produce strings.
Define function that takes a string and removes everything except uppercase letters from it.
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]fst takes a pair and returns its first component. snd takes a pair and returns its second component.
zip It takes two lists and then zips them together into one list by joining the matching elements into pairs.
ghci> zip [1,2,3,4,5] [5,5,5,5,5] [(1,5),(2,5),(3,5),(4,5),(5,5)] ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"] [(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]Notice that because pairs can have different types in them, zip can take two lists that contain different types and zip them up. If the lengths of the lists don’t match, the longer list simply gets cut offto match the length of the shorter one. Because Haskell is lazy, we can zip finite lists with infinite lists:
ghci> zip [1..] ["apple", "orange", "cherry", "mango"] [(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it.(Haskell has type inference.) A type is a kind of label that every expression has.
The :t command which, followed by any valid expression, tells us its type. :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”.
Functions also have types. When writing our own functions, we can choose to give them an explicit type declaration. Here’s how it looks like with a type declaration.
removeNonUppercase :: [Char] -> [Char] removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]The [Char] type is synonymous with String so it’s clearer ifwe write removeNonUppercase : String -> String. Write out the type of a function that takes several parameters.
addThree :: Int -> Int -> Int -> Int addThree x y z = x + y + zThe parameters are separated with -> and there’s no special distinction between the parameters and the return type. The return type is the last item in the declaration and the parameters are the first three. If you want to give your function a type declaration but are unsure as to what it should be, you can always just write the function without it and then check it with :t.
a is a type variable, which means that a can be of any type. Functions that have type variables are called polymorphic functions. Although type variables can have names longer than one character, we usually give them names of a, b, c, d…
A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes.
ghci> :t (==) (==) :: (Eq a) => a -> a -> BoolEverything before the => symbol is called a class constraint.
We can use explicit type annotations. Type annotations are a way of explicitly saying what the type ofan expression should be. We do that by adding :: at the end of the expression and then specifying a type.
ghci> read "5" :: Int 5 ghci> read "5" :: Float 5.0 Enum members are sequentially ordered types—they can be enumerated. The main advantage ofthe Enum typeclass is that we can use its types in list ranges. They also have defined successors and predecesors, which you can get with the succ and pred functions. Types in this class: (), Bool, Char, Ordering, Int, Integer, Float and Double.Bounded members have an upper and a lower bound. All tuples are also part ofBounded if the components are also in it.Num is a numeric typeclass. Its members have the property of being able to act like numbers. To join Num, a type must already be friends with Show and Eq.Integral is also a numeric typeclass. Integral includes only integral (whole) numbers. In this typeclass are Int and Integer.Floating includes only floating point numbers, so Float and Double. A very useful function for dealing with numbers is fromIntegral. It takes an integral number and turns it into a more general number. That’s useful when you want integral and floating point types to work together nicely.Pattern matching consists ofspecifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns. When defining functions, you can define separate function bodies for different patterns. The patterns will be checked from top to bottom and when it conforms to a pattern, the corresponding function body will be used.
Define a factorial function recursively factorial :: (Integral a) => a -> a factorial 0 = 1 factorial n = n * factorial (n - 1)When making patterns, we should always include a catch-all pattern so that our program doesn’t crash if we get some unexpected input. Pattern matching can also be used on tuples.
addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a) addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)You can also pattern match in list comprehensions. Should a pattern match fail, it will just move on to the next element.
Lists themselves can also be used in pattern matching.