Skip to content

Instantly share code, notes, and snippets.

@co89757
Last active August 29, 2015 14:06
Show Gist options
  • Save co89757/052aec10bc64f769687d to your computer and use it in GitHub Desktop.
Save co89757/052aec10bc64f769687d to your computer and use it in GitHub Desktop.
Gist for Haskell

#Start-out Booleans: &&, ||, not , ==, /=(not equal)

Basic functions(prefix or use `` for infix):

succ 8 returns 9

Function syntax

functions can't begin with uppercase letters.

[fname] [...args] = [expression]

Control

if [exp] then [exp] else [exp] else is mandatory

use let [ex] for in-line name-defining.

Lists

[ls]++[ls] for list appending. prepending A:[list] is much cheaper than appending.

[ls] !! index Use !! for list access

List functions

head [lst] to extract the 1st element

tail [lst] to extract the rest of the list except 1st elem.

last [lst] to put the last elem

init [lst] to take all but last elem. length [ls], null [ls] check for emptiness. reverse [ls] take # [ls] take the first # elements drop # [ls] drops the first # elements and returns the list. maximum/minimum [ls] sum/product [] elem E [ls] checks for E's membership in the list

ranges

[2..20] or [2,4,..10] for range with a step

[2,4..] infinite list

list comprehension

[ [expression] | x <- [range] , [condition] ]

Tuples

similar to tuples in C++. can be heterogeneous. fst (a,b) and snd (a,b) are 2 tuple element access functions

a useful function zip (like Py) zip [ls1] [ls2] creates a list with pairs

Typing in Haskell

Haskell is static-typing language. :t FOO use :t to tell the type of an expression. :: means 'has the type of '

Functions also have types. When writing our own functions, we can choose to give them an explicit type declaration.

addThree :: Int -> Int -> Int -> Int  
addThree x y z = x + y + z 

type variable. That means that a can be of any type. This is much like generics in other languages,

ghci> :t head  
head :: [a] -> a

A typeclass is a sort of interface that defines some behavior of a type

ghci> :t (==)  
(==) :: (Eq a) => a -> a -> Bool 

We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

Basic Typeclasses

Eq for equity-checkable

Ord for ordering

Show has a string representation. a useful function: show [exp] like repr in Python. shows the string repr of an expression.

Read and read is the opposite. It interprets given representation and decodes it

ghci> read "True" || False  
True  
ghci> read "8.2" + 3.8  
12.0  
ghci> read "5" - 2  
3  
ghci> read "[1,2,3,4]" ++ [3]  
[1,2,3,4,3]  

type annotation

ghci> :t read  
read :: (Read a) => String -> a

See? It returns a type that's part of Read but if we don't try to use it in some way later, it has no way of knowing which type. That's why we can use explicit type annotations. Type annotations are a way of explicitly saying what the type of an expression should be. We do that by adding :: at the end of the expression and then specifying a type. Observe:

ghci> read "5" :: Int  
5  
ghci> read "5" :: Float  
5.0  
ghci> (read "5" :: Float) * 4  
20.0  

Functions

Pattern matching

sayMe :: (Integral a) => a -> String  
sayMe 1 = "One!"  
sayMe 2 = "Two!"  
sayMe 3 = "Three!"  
sayMe 4 = "Four!"  
sayMe 5 = "Five!"  
sayMe x = "Not between 1 and 5"  

you can also pattern match in list comprehensions.

ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]  
ghci> [a+b | (a,b) <- xs]  
[4,7,6,8,11,4] 

Lists themselves can also be used in pattern matching. You can match with the empty list [] or any pattern that involves : and the empty list

The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.

head' :: [a] -> a  
head' [] = error "Can't call head on an empty list, dummy!"  
head' (x:_) = x 

Notice that if you want to bind to several variables (even if one of them is just _ and doesn't actually bind at all), we have to surround them in parentheses.

tell :: (Show a) => [a] -> String  
tell [] = "The list is empty"  
tell (x:[]) = "The list has one element: " ++ show x  
tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y  
tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y 

you can't use ++ in pattern matches.

Guards

max' :: (Ord a) => a -> a -> a  
max' a b   
    | a > b     = a  
    | otherwise = b 

Where clause

bmiTell :: (RealFloat a) => a -> a -> String  
bmiTell weight height  
    | bmi <= 18.5 = "You're underweight, you emo, you!"  
    | bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"  
    | bmi <= 30.0 = "You're fat! Lose some weight, fatty!"  
    | otherwise   = "You're a whale, congratulations!"  
    where bmi = weight / height ^ 2

We use where clause for local variables and functions

The names we define in the where section of a function are only visible to that function, so we don't have to worry about them polluting the namespace of other functions. Notice that all the names are aligned at a single column. If we don't align them nice and proper, Haskell gets confused because then it doesn't know they're all part of the same block.where bindings aren't shared across function bodies of different patterns see below

initials :: String -> String -> String  
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."  
    where (f:_) = firstname  
          (l:_) = lastname  

Let

The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. Let bindings let you bind to variables anywhere and are expressions themselves, but are very local, so they don't span across guards. Just like any construct in Haskell that is used to bind values to names, let bindings can be used for pattern matching.

cylinder :: (RealFloat a) => a -> a -> a  
cylinder r h = 
    let sideArea = 2 * pi * r * h  
        topArea = pi * r ^2  
    in  sideArea + 2 * topArea

The difference is that let bindings are expressions themselves. where bindings are just syntactic constructs.

They can also be used to introduce functions in a local scope:

If we want to bind to several variables inline, we obviously can't align them at columns. That's why we can separate them with semicolons.

ghci> 4 * (let a = 9 in a + 1) + 2  
42  

ghci> [let square x = x * x in (square 5, square 3, square 2)]  
[(25,9,4)]

ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)  
(6000000,"Hey there!") 

Case of

case expression of pattern -> result  
                   pattern -> result  
                   pattern -> result  
                   ...  

EXAMPLE:

head' :: [a] -> a  
head' xs = case xs of [] -> error "No head for empty lists!"  
                      (x:_) -> x 

Whereas pattern matching on function parameters can only be done when defining functions, case expressions can be used pretty much anywhere

describeList :: [a] -> String  
describeList xs = "The list is " ++ case xs of [] -> "empty."  
                                               [x] -> "a singleton list."   
                                               xs -> "a longer list." 

They are useful for pattern matching against something in the middle of an expression. Because pattern matching in function definitions is syntactic sugar for case expressions, we could have also defined this like so:

describeList :: [a] -> String  
describeList xs = "The list is " ++ what xs  
    where what [] = "empty."  
          what [x] = "a singleton list."  
          what xs = "a longer list."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment