├── README.md ├── 01-GettingStarted.md ├── 03-ValuesAndFunctions.md ├── 04-WorkingWithLists.md └── 02-ProgrammingParadigms.md /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to functional programming in F# 2 | 3 | Materials for ongoing F# courses. 4 | 5 | ## Contents: 6 | 7 | * [1. Getting Started](01-GettingStarted.md) 8 | * [2. Programming paradigms of F#](02-ProgrammingParadigms.md) 9 | * [3. Values and functions](03-ValuesAndFunctions.md) 10 | * [4. Working with lists](04-WorkingWithLists.md) 11 | -------------------------------------------------------------------------------- /01-GettingStarted.md: -------------------------------------------------------------------------------- 1 | # Getting started with F# 2 | 3 | Note: this document will be expanded later. 4 | 5 | ## Installation 6 | 7 | For various options to install F# on your machine, see the 8 | [documentation here](https://docs.microsoft.com/en-us/dotnet/fsharp/get-started/install-fsharp?tabs=windows). 9 | 10 | These tutorials will use [Visual Studio Community 2017](https://visualstudio.microsoft.com/) for presentation purposes. 11 | 12 | ## Using F# interactive 13 | 14 | In Visual Studio or Visual Studio Code, select some code in an `*.fs` or `*.fsx` file and press ALT+Enter 15 | to quickly run/evaluate the selected code part in the F# interactive window. 16 | 17 | You can also write code directly to the F# interactive console that will run when you close your code with a `;;` and press Enter. 18 | 19 | ## Try from a browser 20 | 21 | Go to [fsbolero.io](https://fsbolero.io/) (using Mozilla Firefox gives the best performance). 22 | This is a simple tool that runs completely in your browser to compile and run single file F# projects. 23 | -------------------------------------------------------------------------------- /03-ValuesAndFunctions.md: -------------------------------------------------------------------------------- 1 | # Values and functions 2 | 3 | ## Values 4 | 5 | One of the simplest lines we can write in F# is this: 6 | 7 | ```fsharp 8 | let x = 1 9 | ``` 10 | 11 | We call this a *value binding*, giving a name to a value. 12 | In the same block of code, we can write `x` and it will mean `1`. 13 | For example: 14 | 15 | ```fsharp 16 | let x = 1 17 | x + x // evaluates to 2 18 | ``` 19 | 20 | If we mouse over `x`, we can see `val x: int`. 21 | It means that `x` is a value of type `int` (integer number), this is automatically inferred by the language because `1` is an `int`. 22 | Optionally the type can be specified explicitly, this does not change the meaning of our program, but makes the type clearly visible. 23 | 24 | ```fsharp 25 | let x : int = 1 26 | ``` 27 | 28 | Examples of simple values and types: 29 | 30 | ```fsharp 31 | let a : int = 1 // an integer on 32 bits (range -2,147,483,648 to 2,147,483,647) 32 | let b : float = 1.0 // a floating point number on 54 bits 33 | let c : uint32 = 1u // an unsigned integer on 32 bits (range) 34 | let d : char = 'a' // a single character 35 | let e : string = "hello" // a string - text with any number of characters 36 | let f : bool = true // Boolean - true or false value 37 | let g : unit = () // the type of having no value 38 | ``` 39 | 40 | For a full list of basic types, see the [documentation here](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/basic-types). 41 | 42 | ## Immutability and shadowing 43 | 44 | What we can't do is to change the value of `x`. 45 | In F#, a simple `let` binding is not a variable but a constant declaration. 46 | This won't work: 47 | 48 | ```fsharp 49 | let x = 1 50 | x <- 2 // Error: x is not mutable 51 | ``` 52 | 53 | But we can have `let` bindings inside each other. 54 | 55 | ```fsharp 56 | let y = 57 | let x = 1 58 | x + x 59 | ``` 60 | 61 | Here, while we are calculating the value of `y`, we are naming a value of `x` internally. 62 | This `x` is not visible from the outside. 63 | The *scope* of the `let` binding is just any code below it in the same block or nested block of code. 64 | 65 | ```fsharp 66 | let y = 67 | let x = 1 68 | let x = x + 1 69 | x + x 70 | ``` 71 | 72 | Now, it seems as though the value of `x` is changed from 1 to 2. 73 | But actually, the second `let x =` is just reusing the name `x`, *shadowing* the original value bound to it. 74 | On the final line, `x` has value 2, because it is the closest value tied to the name `x`. 75 | 76 | ## Variables 77 | 78 | To declare *variables*, having values that change over time, we need the extra keyword `mutable`: 79 | 80 | ```fsharp 81 | let mutable x = 1 82 | x <- 2 // No errors now 83 | x + 1 // evaluates to 3 84 | ``` 85 | 86 | ## Functions 87 | 88 | Consider this line of code: 89 | 90 | ```fsharp 91 | let addOne x = x + 1 92 | ``` 93 | 94 | If we mouse over `addOne`, we see a tooltip with `val addOne: x:int -> int`. 95 | This tells us, that `addOne` is also a value, but has type `int -> int` which means it is a function 96 | (we can ignore the parameter names like `x` function type). 97 | 98 | That's right, functions are also a kind of value, this will come in very handy later. 99 | The type of a function value is also called a `signature`, because it is a good descriptor of how it can be used: 100 | what parameter can be passed to it and what it will return. 101 | `int -> int` means this function takes a single `int` parameter and returns an `int`. 102 | 103 | The `x` parameter is automatically inferred to be an `int` from how it is used: it is added to an `int` (`1`). 104 | We can make the types explicit too if we want: 105 | 106 | ```fsharp 107 | let addOne (x: int) -> int = x + 1 108 | ``` 109 | 110 | This is the simplest way we can use this function: 111 | 112 | ```fsharp 113 | addOne 1 // evaluates to 2 114 | ``` 115 | 116 | ## Curried functions 117 | 118 | Let's take a look at a function that takes two parameters: 119 | 120 | ```fsharp 121 | let add x y = x + 1 122 | ``` 123 | It will have a tooltip `val add: x:int -> y:int -> int`. 124 | 125 | This is called a *curried* function (named after Haskell Curry, not the spice). 126 | It can take two `int` values consecutively and will return an `int`. See: 127 | 128 | ```fsharp 129 | add 1 2 // evaluates to 3 130 | ``` 131 | 132 | So if the type of `add` is `int -> int -> int`, what happens if we provide it only one argument? 133 | 134 | ```fsharp 135 | let addOne = add 1 136 | ``` 137 | 138 | The tooltip says `val addOne: (int -> int)`, this shows that what we get is still a function that expects an `int` argument. 139 | This makes sense if you look at `add 1 2` and add some parentheses: `(add 1) 2`. 140 | This evaluates to 3, the same as before, and highlights that passing the value `1` to add results in a function 141 | to which we are passing `2` to get the final result. 142 | So with the `let` binding above, `addOne` is the same as `add 1`, so you can also write `addOne 2` to get `3`. 143 | 144 | ## Pipe operator 145 | 146 | When writing expressions with multiple function calls, often we need parentheses: `add 1 (add 2 3)`. 147 | F# does not require parentheses around arguments but when we want to evaluate a sub-expression to pass to an outside function, we need them. 148 | This can get harder to read if there is a lot of them, so there is a nicer way: 149 | 150 | ```fsharp 151 | 1 |> add 2 |> add 3 // evaluates to 6 152 | ``` 153 | 154 | The `|>` sign is called a *pipe operator*, and what it does is it takes the left hand value and pass it as an argument to the right side function. 155 | So `1 |> add 2 |> add 3` is equivalent to `add 3 (1 |> add 2)` which in turn is the same as `add 3 (add 2 1)`. 156 | The piped version is just easier to read when looking at code: we are taking `1` then adding `2` then adding `3`. 157 | 158 | F# actually defines the pipe operator like this: 159 | 160 | ```fsharp 161 | let (|>) arg func = func arg 162 | ``` 163 | 164 | Operators are just special functions that can be used in infix position (between their two arguments) if they are not parenthesized. 165 | So `1 + 1` could also be written as `(+) 1 1`, where `(+)` works the same as the `add` function we defined for ourselves. 166 | 167 | ## Summary 168 | `let` is the most versatile keyword in F#, defining values with a name. 169 | Functions are just a special kind of value, to which other values can be applied to produce results, 170 | but otherwise they can be stored and passed around just like any other value. -------------------------------------------------------------------------------- /04-WorkingWithLists.md: -------------------------------------------------------------------------------- 1 | # Working with lists 2 | 3 | A `list` in F# is a type that can hold zero or more values of the same `type`. 4 | For example: 5 | 6 | ```fsharp 7 | let numbers = [ 5; 2; 7 ] 8 | let OneToFive = [ 1 .. 5 ] 9 | ``` 10 | 11 | The `1 .. 5` is called a *range expression* and just produces the same list as `[ 1; 2; 3; 4; 5 ]`. 12 | These values are stored in memory as a singly-linked immutable list, here is how we can visualize it: 13 | 14 | ``` 15 | ( 1 | -)--> ( 2 | -)--> ( 3 | -)--> ( 4 | -)--> ( 5 | -)--> () 16 | ``` 17 | 18 | Each chain in the link represents a small object that holds a single value and a *reference* to the next item. 19 | Both parts of the chain are immutable, so we can't change values inside existing lists of modify their ordering. 20 | This guarantees that once we have a list value, until we keep it, its contents remain the same. 21 | 22 | Note that the type of these values are `int list`. 23 | `list` is a generic type so it can be parametrized with another type to mark what type of elements it is containing. 24 | This type can also be written as `list`, but it is possible in F# to put the type parameter first 25 | if there are only one for a generic type (like `list`). 26 | 27 | ## Adding a new item 28 | 29 | Because of this chaining, we cannot add a new item anywhere except at the first *head* position of the list. 30 | This can be done with the `::` operator like this: 31 | 32 | `let moreNumbers = 1 :: numbers` 33 | 34 | Now, `moreNumbers` will hold the values `[ 1; 5; 2; 7 ]`. 35 | Note that the value of `numbers` is unchanged, it is still `[ 5; 2; 7 ]`. 36 | This is because `numbers` is actually a reference to the chain item holding the `5` value. 37 | Going through a list means that we take the current value of a chain item, then go to the next chain item, 38 | take its value and so on, until the last real chain item just links to an empty list end without a value. 39 | 40 | ## Mapping lists 41 | 42 | There is a module named `List` provided by the standard F# library (`FSharp.Core`) that contains a 43 | number of helper functions to work with lists. 44 | 45 | ```fsharp 46 | let square x = x * x 47 | OneToFive |> List.map square // evaluates to [ 1; 4; 9; 16; 25 ] 48 | ``` 49 | 50 | From the example it should be clear what `List.map` does: it applies a function on all elements of a list 51 | and composes a new list from the results. 52 | Remember, lists are immutable, we have guarantee that the original `OneToFive` value is unchanged. 53 | 54 | If we take a look at the tooltip of `List.map` it says: `val map: mapping:('T -> 'U) -> list:'T list -> 'U list`. 55 | This is a more complex function signature than we have seen before but we can break it down. 56 | First parameter to `List.map` is `mapping:('T -> 'U)`, so it expects a function that takes a value of some type 57 | and returns another of some (not necessarily the same) type. 58 | We call `'T` and `'U` type parameters, and any function that has them in their signature is a *generic function*. 59 | Being generic adds a great deal of flexibility to their usage, for example now we are using an `int -> int` mapper 60 | function to transform an `int list` into another `int list` but we can also use it on lists of any other type. 61 | 62 | Still, as before, F# type inference can figure out all of the types involved by itself, but if we want, 63 | we can specify the type parameters ourselves: 64 | 65 | ```fsharp 66 | OneToFive |> List.map square // works the same 67 | ``` 68 | 69 | The parameter to `List.map` is a `'T list`. 70 | So we could write `List.map square OneToFive` and it gives us an `'U list`, which is now an `int list` 71 | because `'U` was already set to `int` by using the `square` function as the mapping parameter. 72 | 73 | Many standard F# functions are set up in a way, that they are taking the value that they are transforming 74 | as the last parameter, so using the pipe operator provides a nicely readable way of applying it. 75 | 76 | ## Anonymous functions 77 | 78 | In the sample above, we defined a `square` function, just to pass it as a mapping parameter to `List.map`. 79 | Often our code could be cleaner if we just have expressions in place instead of defining them with a `let` 80 | on its own line. 81 | Now comes the most fun part of F#: we can define functions in place with the `fun` keyword. 82 | 83 | ```fsharp 84 | OneToFive |> List.map (fun x -> x * x) 85 | ``` 86 | 87 | As a simple rule, if you have a function defined with a `let`, if you replace the keyword with `fun`, 88 | remove the name of the function, and replace the `=` with `->`, then you have an *anonymous function*. 89 | It is also called a *lambda* from their original mathematical notation. 90 | 91 | Let's see a sample using multiple lambdas, `List` functions and piping: 92 | 93 | ```fsharp 94 | OneToFive 95 | |> List.map (fun x -> x * x) 96 | |> List.filter (fun x -> x > 5) 97 | |> List.sum // evaluates to 50 98 | ``` 99 | 100 | Now the advantage of pipes are really apparent, we have a piece of code that is nice to read from top to bottom: 101 | 102 | * we have a list of numbers from one to five 103 | * we make a new list with their squares 104 | * then we make a new list that keeps only the values that are bigger then 5 and drops the rest 105 | * then we sum all the numbers together 106 | 107 | The other big advantage of this structure is that we can select part of the code quickly to evaluate in F# interactive. 108 | For example the first three lines evaluate to `[9; 16; 25]` so we can check where are sum came from. 109 | 110 | let square x = x * x // defining a function 111 | // note that square has type inferred to be `int -> int` 112 | // we now have a named function that we can use in place of the anonymous function (lambda) 113 | 114 | ## Simplifying lambdas 115 | 116 | Using our `add` function from the last lesson (an alias of `+` operator): 117 | 118 | ```fsharp 119 | OneToFive |> List.map (fun x -> add 10 x) // evaluates to [11; 12; 13; 14; 15] 120 | ``` 121 | 122 | We have seen before that `add 10 x` can be also written as `(add 10) x`, so we could have 123 | `fun x -> add 10 x` written as `fun x -> (add 10) x`. 124 | 125 | We are defining an anonymous function here that is doing nothing else than passing its parameter to an inner function. 126 | So actually our lambda does the exact same thing as the inner function and we can simplify our code. 127 | 128 | ```fsharp 129 | OneToFive |> List.map (add 10) // same as above 130 | ``` 131 | 132 | Again, from the original mathematical naming, this simplification is called an `eta-reduction` and 133 | the resulting expression is said to be in *point-free style*. 134 | (What we have as `->` in F# code, is a point in the mathematical notation.) 135 | 136 | ## Folding 137 | 138 | Another common operation with lists is going through the elements, and using each element in some ongoing computation. 139 | Now we would like to get the product of our numbers (multiply them all together). 140 | We have already seen that there is a build-in function for summing all the elements, but there is no such one for product. 141 | This is where `List.fold` helps: 142 | 143 | ```fsharp 144 | OneToFive |> List.fold (fun state item -> state * item) 1 // evaluates to 120 145 | ``` 146 | 147 | `List.fold` takes a `folder:'State -> 'T -> 'State` function as its first parameter. 148 | The first type parameter is named `'State` to tell us that this is the type of our state value: something that contains 149 | the current value of our computation as we go through elements of the list one by one. 150 | The second parameter of `List.fold` is a `state:'State`, the initial state we are starting from. 151 | We use `1` here, because we will multiply numbers together and `1` is the identity element of multiplication. 152 | 153 | What `List.fold` does is to spare us from writing a loop used usually in imperative programming. 154 | It handles a changing value internally, each of our results returned from our lambda serves as the next `state` input, 155 | but we can have this without using any mutable variables. 156 | 157 | We finish with some simplification: the operator `*` can be used like a standard two-parameter function by 158 | using parentheses around it: 159 | 160 | ```fsharp 161 | OneToFive |> List.fold (fun state item -> (*) state item) 1 162 | ``` 163 | 164 | And now we can use eta-reduction on both of our lambda parameters: 165 | 166 | ```fsharp 167 | OneToFive |> List.fold (*) 1 168 | ``` 169 | 170 | What we have here is pretty readable on first sight: we are folding through the given list using multiplication, 171 | starting from 1. 172 | 173 | ## Summary 174 | F# provides the `list` type as a way of dealing with a list of values in the functional way: 175 | processing the items by passing named or anonymous functions to `List` helper functions 176 | instead of having to loop through elements ourselves. -------------------------------------------------------------------------------- /02-ProgrammingParadigms.md: -------------------------------------------------------------------------------- 1 | # Programming paradigms of F# 2 | 3 | F# is a functional-first, multi-paradigm programming language, which means it supports multiple styles of 4 | structuring programs but also has idiomatic way. 5 | We will now look at the main approaches supported by F#. 6 | 7 | ## The problem 8 | 9 | Suppose we want to model a ticket dispenser machine. 10 | First ticket returned should have the number 1, then 2 and so on. 11 | Our programs will always draw three tickets and display those numbers. 12 | 13 | The examples will use increasing levels of abstraction, which makes them more complex as a standalone 14 | code snippet, but provide increasing benefits when used inside a bigger application. 15 | 16 | The goal of these samples are not to teach every part of the code used but to give a higher level understanding 17 | of how computer programs can be written following different styles. 18 | 19 | ## Imperative solution 20 | 21 | ```fsharp 22 | module Imperative = 23 | let private mutable lastTicket = 0 24 | 25 | let getTicket() = 26 | lastTicket <- lastTicket + 1 27 | lastTicket 28 | 29 | let drawOne() = 30 | let ticket = Imperative.getTicket() 31 | printfn "%d" ticket 32 | 33 | do 34 | drawOne() 35 | drawOne() 36 | drawOne() 37 | // prints: 38 | // 1 39 | // 2 40 | // 3 41 | ``` 42 | 43 | With the first, most straight-forward approach, we are creating a single *variable* to hold the value of the last ticket dispensed. 44 | We can initialize it to 0, this means that the next number is 1. 45 | In F#, defining a variable is done with the `let mutable` keywords like so: `let mutable lastTicket = 0`. 46 | This is intentionally verbose, suggesting that the default way of F# is to define immutable (non-changeable) values. 47 | Mutable variables are allowed, but as we will see later, they lack the advantages provided by functional programming. 48 | The type of the variable (`int`) is inferred from its initial value (`0`). 49 | 50 | Then we define a `getTicket` function which takes zero parameters, increases the current value of `lastTicket` by 1 and then returns the new value. 51 | Note that F# is using significant whitespace, meaning that there are no braces or `begin ... end` needed to delimit code blocks, 52 | but the level of code indentation determines the scope of a block (for example the body of a function here). 53 | Also F# is an expression based language, meaning that execution happens like the evaluation of a mathematical formula and not like a series of commands. 54 | Jumping to directly to another code line (goto), or jumping out of a function (return) is not possible, the last value will always be the return value. 55 | Although a function return can have type `unit`, meaning that it does not contain an actual value, in our case we return the value of `lastTicket`, which will be of type `int`, this is inferred automatically. 56 | 57 | The `getTicket` function we have takes no arguments but returns a different number each time. 58 | This shows that it does not behave like a mathematical function, because it is not just using what information is passed to it but also some extra information (the value of `lastTicket`), and also modifies its value. 59 | We call this a *side effect*, the function is doing something else than just computing a return value. 60 | 61 | All of this is enclosed in a *module*, which can serve as a container for related values and functions. 62 | We can add the `private` keyword to the `lastTicket` so that this value is not accessible from outside of the module, otherwise `let` definitions in a module are public. 63 | This adds a guarantee that the value of `lastTicket` cannot be altered from the outside, only by calling `getTicket` which remains public. 64 | 65 | A `drawOne` function outside the module must specify full path of `getTicket` to call it, which includes the module name. 66 | The alternative would be *opening* the module with an `open Imperative` line, which makes all members of the module to be available by just their own name. 67 | This `drawOne` function gets a ticket and prints it to the console. 68 | `printfn "%d"` is taking a single integer number to be printed to the screen. 69 | 70 | The main advantage of the imperative style is its directness and simplicity. 71 | 72 | Disadvantages are: 73 | 74 | * There are no encapsulation of our model of a ticket dispenser machine. This means we cannot create a second dispenser that works independently of the first without duplicating existing code. 75 | * We cannot capture the state of the ticket dispenser. For example if our code is part of a simulation or a game, it provides no way to save its current status, get some more tickets then load back the previously saved status because `lastTicket` is a hidden changing value. 76 | 77 | ## Object-oriented solution 78 | 79 | ```fsharp 80 | module ObjectOriented = 81 | type TicketSource() = 82 | let mutable lastTicket = 0 83 | 84 | member this.GetTicket() = 85 | lastTicket <- lastTicket + 1 86 | lastTicket 87 | 88 | let drawOne (source: ObjectOriented.TicketSource) = 89 | let ticket = source.GetTicket() 90 | printfn "%d" ticket 91 | 92 | do 93 | let source = new ObjectOriented.TicketSource() 94 | drawOne source 95 | drawOne source 96 | printfn "Using a second source:" 97 | let source2 = new ObjectOriented.TicketSource() 98 | drawOne source2 99 | printfn "Switching to back to first source:" 100 | drawOne source 101 | // prints: 102 | // 1 103 | // 2 104 | // Using a second source: 105 | // 1 106 | // Switching to back to first source: 107 | // 3 108 | ``` 109 | 110 | Now we are enclosing the `lastTicket` value and `getTicket` function in a *class*. 111 | It is a way of bundling together values and functions so that multiple instances of it (*object*) can be created. 112 | Not that in F#, a `let` declaration inside a class is always private (different from modules), and to expose functions to the outside, we must make it a *member* of the class. 113 | It is customary in .NET to have member names starting with capital letter, so we are also renaming it to `GetTicket`. 114 | 115 | The empty parentheses in the `type TicketSource()` signify that an instance of this class can be created without any parameters. 116 | So now, before we can call the `GetTicket` member, we need an instance, provided by `let source = new ObjectOriented.TicketSource()`. 117 | Then `source.GetTicket()` is a call to the `GetTicket` member of the `source` object. 118 | We are again combining drawing a ticket and printing it to the console in a `drawOne` function. 119 | 120 | The main advantage of object-oriented programming is code reuse and encapsulation. 121 | We can also use another instance `let source2 = new ObjectOriented.TicketSource()` and then `source.GetTicket()` and `source2.GetTicket()` can be called independently, each providing the next ticket based on its own state. 122 | Also, we class definitions can name a base class, inheriting all of its members and usually adding more or re-defining (overriding) some. 123 | This is also a good way of code reuse, writing less to have the program do what we need. 124 | 125 | Disadvantage: 126 | 127 | * Still, we cannot capture the state of the ticket dispenser object. The `lastTicket` is a hidden changing value inside any `TicketSource` instance. 128 | 129 | ## Functional solution 130 | 131 | ```fsharp 132 | module Functional = 133 | type TicketSource = 134 | { LastTicket : int } 135 | 136 | let getTicket source = 137 | let currentTicket = source.LastTicket + 1 138 | currentTicket, { LastTicket = currentTicket } 139 | 140 | let newTicketSource() = 141 | { LastTicket = 0 } 142 | 143 | let drawOne source = 144 | let ticket, newSource = Functional.getTicket source 145 | printfn "%d" ticket 146 | newSource 147 | 148 | do 149 | let source = 150 | Functional.newTicketSource() 151 | |> drawOne 152 | |> drawOne 153 | printfn "Saved current source state." 154 | source |> drawOne |> ignore 155 | printfn "Using saved source state:" 156 | source |> drawOne |> ignore 157 | // prints: 158 | // 1 159 | // 2 160 | // Saving current source state. 161 | // 3 162 | // Using saved source state: 163 | // 3 164 | ``` 165 | 166 | We are now creating a *record* type to hold a ticket dispenser state. 167 | F# records allow storing any number of related values together which are as default immutable (cannot be changed). 168 | We only need one value stored, `LastTicket`, but having a named record to hold it is a good way to mark its intended use, 169 | so it cannot be swapped by mistake with any other integer value our program might handle. 170 | 171 | Our `getTicket` function will now get a `TicketSource` record to work with and it needs to return two values, 172 | the ticket number drawn, and a new `TicketSource` record. 173 | Because the `TicketSource` record is immutable, we cannot change its contents, a new record object must be created to hold the new state. 174 | 175 | With this approach, the `getTicket` function has no side effects. 176 | It is like mathematical function: pass it the same arguments and you get back the exact same results every time. 177 | We call this a *pure function* in programming terms. 178 | 179 | Printing to the console is a side effect, so our `drawOne` function is not pure. 180 | We can get benefits from pure functions but they are not suitable for everything, so a good idea is to have pure functions for handling data and doing calculations, but have non-pure ones for interacting with the outside world (including printing output or asking user input, reading or writing a file, sending web requests). 181 | The `drawOne` function still returns the new state record, so we can chain multiple of them easily. 182 | 183 | In the main block we are creating a ticket source, then with the `|>` *pipe operator* we are passing it to `drawOne` then again pass the resulting new source to `drawOne`. 184 | This is withing a block started with `let source =` so the final result stored under the name `source` is the result of the second call to `drawOne`. 185 | It is shown when we use this same source twice to call `drawOne` (and ignore the result), the program prints the same ticket number (3) twice. 186 | 187 | Here we see a main advantage of functional programming: we can easily capture the state of our program and reproduce some step multiple times if needed. 188 | Also, using pure functions whenever possible makes us pass along any changing state explicitly. 189 | In a complex program with pure functions, it is much easier to see in what order we can use our functions and pass the result to the next one compared to 190 | an imperative/object-oriented approach where functions have side effects and calling them in wrong order can lead to bugs. 191 | The functional paradigm helps in multiple ways to achieve correctness. 192 | 193 | ## Summary 194 | 195 | We have seen that imperative, object-oriented and functional programming are all possible to do in F#. 196 | There are distinct advantages and disadvantages to all, but F# is designed around making the most of functional programming, so structuring larger programs in this way is highly recommended. --------------------------------------------------------------------------------