Haskell Notes

1 Introduction

1.1 So what’s Haskell

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.

1.2 What you need to dive in

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.

2 Starting out

2.1

  • + works only on things that are considered numbers,== works on any two things that can be compared. They both have to be the same type of thing.
    * is an infix function.
  • In Haskell, functions are called by writing the function name, a space and then the parameters, separated by spaces.
  • The succ function takes anything that has a defined successor and returns that successor.
  • Function application (calling a function by putting a space after it and then typing out the parameters) has the highest precedence of them all.
  • If a function takes two parameters, we can also call it as an infix function by surrounding it with backticks.
92 `div` 10

2.2 Baby’s first functions

  • Functions are defined in a similar way that they are called. The function name is followed by parameters seperated by spaces.
    doubleUs x y = 2*x + 2*y

2.2.1 If statement

doubleSmallNumber x = if x > 100
						then x
						else x*2

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) + 1

That 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 letters
  • When 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!"

2.3 An Introduction to lists

  • In Haskell, lists are a homogenous data structure. It stores several elements of the same type.
  • We can use the let keyword to define a name right in GHCI.
ghci> let lostNumbers = [4,8,15,16,23,42] 
ghci> lostNumbers
[4,8,15,16,23,42]

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.

2.3.1 ++ , : , !! operator

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.2

2.3.2 Some properties about List

Lists 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.

2.3.4 Some basic functions that operate on lists

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.

operations on [1, 2, 3, 4, 5]

  • 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.
    在这里插入图片描述

2.4 Ranges

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.

2.4.1 Some functions that produces infinite lists

  • cycle takes a list and cycles it into an infinite list. If you just try to display the result, it will go on forever so you have to slice it off somewhere.
  • repeat takes an element and produces an infinite list of just that element.
    Note: It’s simpler to just use the replicate function if you want some number of the same element in a list.
    在这里插入图片描述

2.5 list comprehension

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']]

在这里插入图片描述

2.6 Tuples

2.6.1 Tuples and List

  • Tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences.
    Tuples are used when you know exactly how many values you want to combine and its type depends on how many components it has and the types of the components. They are denoted with parentheses and their components are separated by commas.
    Another key difference is that they don’t have to be homogenous.
    e.g. [(1,2), (8,11), (4,5)]
  • Use tuples when you know in advance how many components some piece of data should have. Tuples are much more rigid because each different size of tuple is its own type, so you can’t write a general function to append an element to a tuple.
  • Tuples can be compared with each other if their components can be compared

2.6.2 Two useful functions that operate on pairs(tuple with two components):

fst takes a pair and returns its first component.
snd takes a pair and returns its second component.

2.6.3 A cool function that produces a list of pairs: zip

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")]

3 Types and Typeclasses

3.1 Believe the type

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.

3.1.1 use GHCI to examine the types of some expressions

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”.
在这里插入图片描述

3.1.2 The type of functions

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 + z

The 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.

3.1.3 An overview ofsome common types

  • Int stands for integer. Int is bounded, which means that it has a minimum and a maximum value.
  • Integer stands for integer. The main difference is that it’s not bounded so it can be used to represent really really big numbers.
  • Float is a real floating point with single precision.
  • Double is a real floating point with double the precision
  • Bool is a boolean type. It can have only two values: True and False.
  • Tuples are types but they are dependent on their length as well as the types oftheir components

3.2 Type variables

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

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…

3.3 Typeclasses

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 -> Bool

Everything before the => symbol is called a class constraint.

3.3.1 Some basic type classes

  • Eq is used for types that support equality testing. The functions its members implement are == and /=.
  • Ord is for types that have an ordering. All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members ofthe same type and returns an ordering. Ordering is a type that can be GT, LT or EQ.
ghci> "Abrakadabra" < "Zebra" 
True 
ghci> "Abrakadabra" `compare` "Zebra" 
LT
ghci> 5 >= 2 True ghci> 5 `compare` 3
GT
  • Members of Show can be presented as strings. All types covered so far except for functions are a part of Show. The most used function that deals with the Show typeclass is show. It takes a value whose type is a member of Show and presents it to us as a string.
  • Read is sort of the opposite typeclass ofShow. The read function takes a string and returns a type which is a member of Read.
ghci> read "True" || False 
True 
ghci> read "8.2" + 3.8
12.0

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.

4 Syntax in Functions

4.1 Pattern matching

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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值