165 lines
6.6 KiB
Markdown

# What the Hell am I doing?
Learning about GHCI!
One caveat: doing negative numbers
5 * -3 will cause issues because it's not clear what '-' actually represents...
Does it mean subtraction or negation?
5 * (-3) fixes this issue.
## Boolean Stuff
'&&' means 'and'
'||' means 'or'
'not' is a negation
Types are also the law of the land here. You can't mix and match types across operations they're not specified for already.
This makes sense and is intuitive. Some expressions like '5' can take multiple types though, for example being an integer or a float.
## A quick note on functions
*infix* functions are those that go in between arguments
- e.g 5+10
- or 2*2.3
*prefix* functions are those that go *before* arguments. They are more common with non number types
- succ 0
- min 9 10
You can also make prefix functions with two arguments into infix functions when you want. Here's an example
- div 92 10
This integral divides 92 by 10, yielding 9. But at first glance, it is not clear what is the divisor. So instead we can write
- 92 'div' 10
This is easy and more clear for some applications.
# Baby's First Functions
1. I created my first Haskell program! It is called 'baby.hs'
2. It gets loaded into GHCI by doing ':l baby'
3. Now I created 'doubleUs', and loaded it the same way
I notice that things are 'a' type. They accept floats and integers no problem...
4. Now, we can rewrite 'doubleUs'
```haskell
doubleUs x y = doubleMe x + doubleMe y
```
This makes things flexible! We know too that doubleMe is obviously correct, so we don't have to spend more time making sure our implementation in doubleUs is correct too.
Notably, functions are not ordered in a Haskell script. It does not matter if doubleUs comes before or after doubleMe.
## if - then - else
Haskell REQUIRES an else statement. This is because functions are complete, and therefore they must return *something*.
## Function Names are Funky
also, ' is a perfectly valid character in naming things in Haskell. It is usually used for a couple of reasons
1. To denote that a function is 'strict', that is to say not lazy
2. To denote a slightly modified version of a function
The other notable thing about function names: they can't begin with capital letters.
Functions that don't take any parameters are **definitions** or **names**.
# An intro to lists
Lists in Haskell are *homogenous*. Every element in a list is the same data type.
Lists can be easily concatenated with '++'. This has a weird feature though in that Haskell cycles through the whole list on the LHS before concatenating. This can take a long time with bigger lists.
':' can sometimes solve this issue by putting the thing to be added right at the front of a list. This is instantaneous in comparison.
Lists can be indexed by '!!'. Indexes start at 0.
Lists, if their elements can be compared, are comparable. They are an 'all' comparison by default, first from the head and then by each element therafter. They return a SINGLE true or false.
## Here's some basic list operations:
Head: The first entry
Tail: Everything but the head
Init: Everything but the last
Last: Take a guess, Einstein.
length: says the length!
null: checks if the list is empty ('[]')
reverse: Reverses the list
take n: takes n first elements from the list. If n is greater than length(list), it only returns the whole list
drop n: does the opposite of take, drops n elements from the beginning of the list
maximum or minimum: expected
sum, product: expected
elem thing list: elem takes a thing, and looks for it in the list. If it is, it returns true. if not, false.
## Ranges
Ranges can be created using '..'.
[1..20] is all integers from 1 to 20
['a'..'z'] is all letters from a to z
Ranges can also be a little more clever and use a step:
[1,2..20] gives all EVEN numbers 1 to 20.
[3,6..20] gives all multiples of 3 between 3 and 20.
[1,2,4,8,16..100] will NOT work however, because you cn only specify one step.
Ranges don't work backwards by default. A step must be specified:
[20..1] FAIL
[20,19..1] pass!
Floats can get funky in ranges
[0.1, 0.3..1] yields [0.1,0.3,0.5,0.7,0.899999999999999,1.099999999999999999]... weird.
Infinite ranges are also a thing. [13,26..] is a totally valid statement. Because Haskell is 'lazy', we can say take 24 [13,25..] and get the first 24 elements of this range.
cycle takes a list and cycles it into an infinite list. This let's take work ad infinitium.
repeat does a similar thing but only with one element. But replicate does this too and is arguably easier
replicate n x = [x,x..x] where length [x,x..x] = n
## List Comprehensions
They're very similar to set building notation.
```haskell
-- instead of the following
take 10 [2, 4..]
-- we can write
[x*2 | x <- [1..10]]
-- we can also add predicates
[x*2 | x <- [1.10], x*2 >= 12]
-- will only yield even numbers greater than 12 in the first 10 elements of the set
```
boomBangs is an interesting one. We can write code that does a function as the function, while also incorporating predicates.
An easy way to remember: [Function | Set, Predicate, (predicate 2),...]
Fucking Stupid Predicates
We can also use multiple lists. If multiple lists are given (one for each variable), then all combinations of the two lists are used.
Strings are just lists. We can apply list comprehensions to them!
Here's a couple of useful examples:
```haskell
-- Now Writing our own custom list function
length' xs = sum [1 | _ <- xs]
-- use dummy variable _, draw an element from a list and replace it with 1. Sum all of those.
--- Strings are lists! We can use list comprehensions on them.
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
```
Finally, list comprehensions can be nested. So we can use a comprehension to extract a list from a set of lists, and then use another comprehension on that lsit itself.
## Tuples
Tuples are somewhat confused with lists, but they are not exactly the same.
Tuples are useful in specific cases. Namely, when an explicit number of elements and their types are known. Tuples are designated by parentheses '()'
Also interestingly, tuples can have a mish mash of types inside them. The specific combination of types inside a tuple makes the tuple itself a certain type:
```haskell
[(1,2), (3,4), (5,6)] -- is okay!
[(1,2), (8,5,11), (5,6)] -- is NOT okay!
[(1,2), ('Okay?', 2)] -- is NOT okay!
```
Tuples have special names for their sizes:
(1,2) - Pair
(3,4,5) - Triple
(4,5,6,7) - 4-tuple
(2,3,..,n) - n-tuple
Here's some useful tuple functions:
fst: takes a pair and returns first component
snd: Suprise! gives the second component of a pair
zip: Takes two lists and makes pairs of the elements. Stops at the shorter list's end.