├── Lessons ├── assets │ └── pattern-matching-patterns.png ├── day-20-micro-lesson.md ├── day-01-tuples.md ├── day-02-functions.md ├── day-21-type-aliases.md ├── day-13-order-of-code-and-files.md ├── day-03-curried-functions.md ├── day-22-function-type-aliases.md ├── day-04-pipe-operator.md ├── day-18-pattern-matching-part-3.md ├── day-05-everything-is-an-expression.md ├── day-06-fsharp-syntax-and-more-functions.md ├── day-11-single-case-unions.md ├── day-16-lists-part-1.md ├── day-08-data-and-behaviour.md ├── day-14-pattern-matching-part-1.md ├── day-15-pattern-matching-part-2.md ├── day-09-discriminated-unions.md ├── day-07-compiler-'magic'-and-even-more-functions!!.md ├── day-10-modules.md ├── day-17-lists-part-2.md ├── day-12-single-case-unions-as-marker-types.md └── day-19-pattern-matching-part-4.md ├── LICENSE └── README.md /Lessons/assets/pattern-matching-patterns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jmaharman/fsharp-a-beginners-guide/HEAD/Lessons/assets/pattern-matching-patterns.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 John Harman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lessons/day-20-micro-lesson.md: -------------------------------------------------------------------------------- 1 | # Day 20: micro-lesson 2 | I have generally used `Option` and `List` in the form you are used to in C#, but in F# they are more often described as below: 3 | 4 | ```fsharp 5 | type Customer = { 6 | Email : string option 7 | Orders : string list 8 | } 9 | // Equivalent to the above 10 | type CustomerAlt = { 11 | Email : Option 12 | Orders : List 13 | } 14 | ``` 15 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAwgrgZ2AewLYQE5QLxQN4CwAUFKVAKKoCGAlgDZQBcUSGNAdgOZTJjA3J2xMlADyGACaYETFsDZcodGkmIBfYsQD0WigEc4NAG5U6EdsCgorAC2hUARsiMRioSLEQp0GAIJ1LXEISMkpaBmZRPgF2AB5WDk4APmEycSkMGWYAGRVgePlElKINImIzSwBjLwBGWXgkNEwcfFTSMPoWgGUmqAAiACtkG3YAAQQAaxAAdypJAFoEZDo4fkEEADpKtD62sUlpFoBtfvTmmr6AXXVNIgqoaqQAJnqvJr8AluCRDoZcHvQ-SGI3GU1mCyWKzW7E221QuxCpDOmWOpwOWAu1xKtzACmAADN+r8oNRgJUbMwAKQOPpQAAUj2ANQ2xNwjKeLOo9AAlMRcRwCWiMjJSeSqTT6YzmciZGyvByZdygA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 16 | 17 | If you try and compare the types `Customer` directly, F# won't let you. Feel free to try that. 18 | 19 | ```fsharp 20 | printf "Customers match: %b" (cust1 = cust2) 21 | ``` -------------------------------------------------------------------------------- /Lessons/day-01-tuples.md: -------------------------------------------------------------------------------- 1 | # Day 1 - Tuples 2 | 3 | First we'll start with what you already know from C#, and today that is a tuple. 4 | 5 | A tuple acts in exactly the same way in both F# and C#, but F# provides a simpler syntax for creating them: 6 | 7 | ```fsharp 8 | let tuple = 1, 2 9 | ``` 10 | 11 | You probably noticed the use of let, which you may recognise from other languages as a variable declaration. It is F#'s equivalent of `var`, but an important difference is that the variable is immutable by default. 12 | 13 | Getting values out of a tuple is a little different to C#. However, to those familiar with C# tuple de-structuring, F# follows a similar pattern: 14 | 15 | ```fsharp 16 | let one, two = tuple 17 | 18 | let three, four = (3, 4) 19 | ``` 20 | 21 | In fact as a rule of thumb I've noticed that commas are only ever used when a tuple is involved. I may be wrong there, but it does seem to be correct from what I've learned so far. You can see it above when creating the tuple, and you can see it in the second example when deconstructing the tuple into multiple variables. 22 | 23 | If I only wanted one of the values out of the tuple, then I could use an underscore to ignore the second value. 24 | 25 | ```fsharp 26 | let onlyOne, _ = tuple 27 | ``` 28 | 29 | It should be noted that the F# standard library has the functions `fst` and `snd` defined so the following will achieve the same result: 30 | 31 | ```fsharp 32 | let onlyOne = fst tuple 33 | let onlyTwo = snd tuple 34 | ``` 35 | 36 | Day one complete, congratulations. Feel free to play around with the above here: 37 | 38 | [Try the code](https://try.fsharp.org/#?code=LAKANgpgLgBFCuAHSMC8MCMAaGAmUoiATgJYB2UAZjAEQAqAFhHEpAFwwCkAgjS8hALhoMAPZkIOKAHdRafpFCRYUBkQiSYlUfCLyAFAGYcAFgCUQ4uSq0AyhEQBDIo6jMAbo7DwIAZw6cACYwjmTBQXzizDKiQspiZGAAngDyElgA+vIIApakFNQ0aclwTAkeXj4BgZGJqRJxIuLJdLLyvmEKgiCE+TZFdaXRbZ7eENW1LbJAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 39 | -------------------------------------------------------------------------------- /Lessons/day-02-functions.md: -------------------------------------------------------------------------------- 1 | # Day 2 - Functions 2 | 3 | Today's lesson we will start on functions. Functions are first class citizens in F#. This means that functions are regular values just like other variables. In C# there are methods which are the functions you define on your classes, static or otherwise. You also have `Func` and `Action`, which are what you pass into methods like Linq `Select`, `Where`, statements. A method is different to a `Func` or `Action`, although it can be cast to one when necessary. Those coming from C# know how to create methods in the following manner: 4 | 5 | ```csharp 6 | public class Example 7 | { 8 | private int Sum(int x, int y) 9 | { 10 | return x + y; 11 | } 12 | 13 | public void UsingDifferentFunctionsTypes(int inputX, int inputY) 14 | { 15 | // You have to define the inputs and outputs first when declaring as a func variable 16 | Func variableSum = (x, y) { 17 | return x + y; 18 | } 19 | // Local functions allow you to define them as you would normally, except only available the current scope 20 | int LocalSum(int x, int y) { 21 | return x + y; 22 | } 23 | You can call each of them in the same way 24 | Console.WriteLine(UpperCaseMethodGroup(inputX, inputY)) 25 | Console.WriteLine(InnerFunction(inputX, inputY)) 26 | Console.WriteLine(variableFunc(inputX, inputY)) 27 | } 28 | } 29 | ``` 30 | 31 | F# functions are first class citizens in the F# language. There are two types of functions: `curried` and `tupled`. The following `tupled` function is more familiar coming from C# and other _C Style_ languages: 32 | 33 | ```fsharp 34 | // Function definition 35 | let sum(x, y) = x + y 36 | let result = sum(1, 2) 37 | 38 | printf "%d" result 39 | ``` 40 | 41 | [Try the code](https://try.fsharp.org/#?code=LAKA9GAEBiCuB2BjALgSwPb0gEwKYDNV5U1NQAbXZSAZ1gFsAKADwBpIBPASkgF5JmkANSdQEGAhQYssGgEMA5rgpVIAJ1x1y1fnSYBGdgCYuoUAAc1RZPkgAiAKTY76zbG1A&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 42 | 43 | You may notice that there is no need to define the types of those arguments, that is because the F# compiler can infer the types you want based on how you use them. In this case the fact you are using the + operator on them gives the compiler a hint that they are integers. That may be scary at first, but don't worry, when looking at it in an IDE it will tell you what the signature is, often shown above in something similar to this: 44 | 45 | ```fsharp 46 | int * int -> int 47 | let sum(x,y) = x + y 48 | ``` 49 | 50 | I won't explain the function signature just yet, but know that you can decipher what is expected in your function by reading that one line. 51 | -------------------------------------------------------------------------------- /Lessons/day-21-type-aliases.md: -------------------------------------------------------------------------------- 1 | # Day 21: Type aliases 2 | Type aliases allow you to alias, go figure, an existing type. We do not create a new type, it's merely a shortcut to the existing type. 3 | The syntax looks like this: 4 | 5 | ```fsharp 6 | type [typename] = [existingType] 7 | // e.g. 8 | type CustomerId = string 9 | ``` 10 | 11 | C# can do aliases too, but you have to declare them in every file you would like to use them, which makes them harder to use. In F# you can re-use them as often as you like. 12 | 13 | ```csharp 14 | using CustomerId = System.String; 15 | ``` 16 | 17 | Why is this helpful? A few reasons really. Take the below example: 18 | 19 | ```fsharp 20 | type CubeDimensions = double * double * double 21 | type Package = { 22 | Dimensions : CubeDimensions 23 | } 24 | let dimensionsTuple = 12.1, 10.5, 100.5 25 | let package = { 26 | Dimensions = dimensionsTuple 27 | } 28 | ``` 29 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAwgrgIwgEQJYFsIDsDOqD2uUAvFACb6IA20AVOZQjVPRdRALABQ3okUABQCGAYwDWQgObRSAb25RFUNJlwEiALliIUGbHkI5uAX27cawcnrWGAKnDDNSARgBMAOmcAaKM4AM7gCsPv4BgWZcFlBgohLSJFDyXErK1gZEpGRp6jj2jpxcpjxcQA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 30 | 31 | Here we've not created any special constraints around the cube dimensions, but it's made the Package type a little more readable. You only need to look into what `CubeDimensions` are if you are going to interact with them, at which point you can mouse over and see what the definition is. The alternative is reading through the code and taking a second to realise that the tuple3 of double is a representation of the cube's dimensions. 32 | 33 | To prove there are no new types involved here, you'll notice that `dimensionsTuple` is created as a standard tuple, it doesn't reference `CubeDimensions` at all. It's perfectly valid to assign that to the `Dimensions` property. 34 | 35 | Being lazy devs, when we find a long generic it can be a pain to type these values out and being C# we have to declare these types everywhere. While F# doesn't require us to always define our types up front, sometimes it can help. Type aliases can save us some keystrokes in these situations too. Imagine you are communicating to a remote server: 36 | 37 | ```fsharp 38 | type ServerError = 39 | | ValidationError of string list 40 | | AuthError of string 41 | | Exception of string 42 | type ServerResponse<'a> = Async> 43 | ``` 44 | 45 | 'a is the type you are expecting in response, and the DU is a list of possible `ServerErrors`. The request would be `Async`, and your response from the server would either be `Result.Ok` (`'a`, the left generic) or `Result.Error` (`ServerError`, the right generic). 46 | 47 | Using ```ServerResponse``` is far simpler to consider, than the full type of ```Async>```. When reading your code you can take it at face value that you will get the value you are after, but you can also dive into what else may go wrong. 48 | 49 | While it is possible to create simple aliases in C#, you cannot create typed aliases because the whine at you. 50 | 51 | ```csharp 52 | using CustomList = System.Collections.Generic.List; 53 | ``` 54 | 55 | Even if you could it still wouldn't be as useful, because as we said above, you have to copy those aliases into every file where you use them. -------------------------------------------------------------------------------- /Lessons/day-13-order-of-code-and-files.md: -------------------------------------------------------------------------------- 1 | # Day 13: Order of code and files 2 | A light lesson for the day, but no less important. 3 | 4 | You may not have realised so far, but the order that you write your code means everything. Generally speaking any code that you wish to reference should be declared above it in the order of the project. For example, if we rehash the `Address` record type from the other day, but declare the SUs below it, the compiler will error. 5 | 6 | ```fsharp 7 | type Address = { 8 | Line1 : Line1 9 | Road : Road 10 | City : City 11 | PostCode : PostCode 12 | Country : Country 13 | } 14 | type Line1 = Line1 of string 15 | type Road = Road of string 16 | type City = City of string 17 | type PostCode = PostCode of string 18 | type Country = Country of string 19 | ``` 20 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuUymg4qAJQHsBDOe97uC2IBhMqD6jQQogAUOKYMI5xoDOQqUrpUJQFcKwRCAkd9hkAQC+BAqEiMqtLOUdQOAMygLElAOa3waE4eTH4Qjy9DPwD7SWMsOLdPb2j8O2h1RWVoLEzNaAiUin80wJ1TAyNQvUrjQqjioA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 21 | 22 | As you'll see when you go to the above, it doesn't compile. If you really really wanted to have the other types defined below instead you can use the and keyword, but from what I've seen it's not that common and I don't think it helps readability. 23 | 24 | ```fsharp 25 | type Address = { 26 | Line1 : Line1 27 | Road : Road 28 | City : City 29 | PostCode : PostCode 30 | Country : Country 31 | } 32 | and Line1 = Line1 of string 33 | and Road = Road of string 34 | and City = City of string 35 | and PostCode = PostCode of string 36 | and Country = Country of string 37 | ``` 38 | 39 | It's all well and good saying that in a single file the types / functions you reference must have been defined already, but how does that work when your files are ordered alphabetically on the file system? Well it turns out that they aren't. Files in an F# project are ordered as you define them. This may feel like a step backwards but it brings a lot of benefits. 40 | 41 | If I gave you an C# project to start exploring, where would you head for? Program.cs probably, or perhaps Startup.cs. What if it is a library, where would you head to then? In F# there is only one place to go, and that's the last file of the project. In there will be the entry point or public API functions of your library. 42 | 43 | That's a small benefit, the big benefit comes in how it forces you to consider the structure and dependencies of your application as you develop, without slipping into accidental cyclic dependencies, or quick wins by referencing the presentation infrastructure in your business logic. 44 | 45 | Another aspect of project structure that will feel strange for a C# developer is the fact that in F# you will have many types, along with related modules, within one file. A juxtaposition to the C# world of one class per file. This is mostly because the syntax of F# is so light, that it would be crazy to split out every type into a new file. As you've we can create what would be quite complex models and functions in under 20 lines of code. It's worth pointing out that we aren't aiming to write as few lines of code as possible, but we are aiming to only write as many as we need to. 46 | 47 | when managing F# code you'll often think about files, rather than folders. -------------------------------------------------------------------------------- /Lessons/day-03-curried-functions.md: -------------------------------------------------------------------------------- 1 | # Day 3: Curried Functions 2 | 3 | Yesterday you saw the below function (now presented with signature alongside it): 4 | 5 | ```fsharp 6 | let sum(x, y) = x + y // int * int -> int 7 | ``` 8 | 9 | It's probably fairly easy to read for you chaps because you are used to seeing parameters provided with this syntax. The more observant of you may have noticed I said that a rule of thumb with commas in F# is that they are generally only used when a tuple is involved, and that is exactly the case here. This type of function is called a Tupled Function. 10 | 11 | The ```*``` in the function signature denotes a tuple, hence the name, and in this case it is showing a tuple expecting two ints. The more parameters you add to the function signature, the larger that tuple would grow. For example: ```int * string * long``` translate to a ```(int, string, long)``` in C# 12 | 13 | Now let's look at the more widely used syntax for functions in F#, the Curried Function. 14 | 15 | ```fsharp 16 | let sum x y = x + y // int -> int -> int 17 | ``` 18 | 19 | I'm sure your first thought is "WTF is that!". I admit it does take a bit of getting used to but once you start using it, it becomes second nature pretty quickly. Let's break it down quickly. After let is the function name "sum", there after each parameter is separated by a space "x" and "y", with the equals then denoting the start of the function body where x + y are added, exactly the same as before. 20 | 21 | So why would F# developers use a Curried Function over a Tupled Function? The secret lies in the fact that a Curried Function does not need to have all it's arguments provided to it at once, because when you omit an argument a new function is returned instead, with the remaining arguments as inputs, for example: 22 | 23 | ```fsharp 24 | let sum x y = x + y // int -> int -> int 25 | let addFive = sum 5 // int -> int 26 | let result = addFive 10 27 | printf "%d" result // 15 28 | ``` 29 | 30 | The add5 function has now got the value 5 baked in as the first parameter to the sum function. If you inspect the signature of the add5 function, you'll see that one of the "int ->" is missing, because 5 is now always provided for "x". 31 | 32 | If you considered the equivalent in C#, it could look something like this using Local Functions: 33 | 34 | ```csharp 35 | int Sum(int x, int y) { 36 | return x + y; 37 | } 38 | int AddFive(int z) { 39 | return Sum(5, z); 40 | } 41 | var result = AddFive(10); 42 | ``` 43 | 44 | You may or may not see the power in this composition tool yet, but hopefully over the coming weeks it will become more apparent as to why it is used. 45 | 46 | On top of that we'll also come to see why the lighter syntax of F# gives more benefits over the C# version above, which at the moment is not too dissimilar. You can of course write the C# version as an expression body using a lambda instead, but generally C# code is imperative and so would require the brackets around it to help the logic flow. 47 | 48 | So to recap, Tupled Functions force you to supply all your arguments at once (like C#) 49 | Curried Functions allow you to supply only some of the require arguments, where by a new function is returned with the remaining parameters expected. 50 | 51 | [Try today's code](https://try.fsharp.org/#?code=DYUwLgBAzgrgthAHhAnhAvEiBqVED0+EAlgHaQC0AfCeRNbWALABQokAhgCZcBixANxAZo8CAFYCRMpRozW7CACcQsYJEzc+g4QEYADKwAOSmQDMIAIgCkXS8tUx1UiLvFA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) -------------------------------------------------------------------------------- /Lessons/day-22-function-type-aliases.md: -------------------------------------------------------------------------------- 1 | # Day 22: Function type aliases 2 | 3 | You thought I was done with type aliases didn't you? Didn't you! Well I'm not, because I didn't show you how to alias a function. Hopefully the idea of this is no surprise, functions are a first class citizen in F# after all. Let's bring back the `CubeDimensions` alias, and use it in a function signature: 4 | 5 | ```fsharp 6 | type CubeDimensions = double * double * double 7 | type CalculateVolume = CubeDimensions -> double 8 | ``` 9 | 10 | We've not seen a function signature for a while, hopefully can still remember the structure. If not here's a quick recap. The last item in the list is always the output, everything before that is an input with each item being separated by a ```->```. A function must have _at least one_ input, and *must have one* output. 11 | 12 | Let's expand on the above and say that the `CubeDimensions` aren't provided to us semantically, but as a string. We'll need a function to parse a string and output a `CubeDimension`, we can define that as such: 13 | 14 | ```fsharp 15 | type ParseCubeDimensions = string -> CubeDimensions 16 | ``` 17 | 18 | As we think about this signature, we'll probably start to think of scenarios that we need to consider and decide whether they are worth taking into account or not. In essence giving ourselves a guideline of what we are going to implement. 19 | 20 | For example, as I've written the function signature above I realise I've not handled the failure path, what happens if the string isn't valid? Maybe I should make it an option 21 | 22 | ```fsharp 23 | type ParseCubeDimensions = string -> CubeDimensions option 24 | ``` 25 | 26 | The problem with option is that my client would like feedback as to why the parsing failed. This means that my output naturally becomes a `Result`: 27 | 28 | ```fsharp 29 | type ParseCubeDimensions = string -> Result 30 | ``` 31 | 32 | What should our `ParseError` look like? We can start hashing out some useful scenarios that would be good to feedback to the user pretty quickly. 33 | 34 | ```fsharp 35 | type ParseErrors = 36 | | InputEmpty 37 | | UnexpectedFormat of UnexpectedFormat 38 | | MissingDimensions of MissingDimension list 39 | | UnitOfMeasureNotSupported of string 40 | ``` 41 | 42 | Before long you've created a pretty comprehensive set of types that describe the process from start to finish. All this with very few lines, and you've not even started implemented any code yet. 43 | 44 | NB: I have used a variety of types below, feel free to jump back to any previous lessons to refresh your memory if necessary: 45 | 46 | ```fsharp 47 | type Measurement = Measurement of double 48 | type UnitOfMeasure = UnitOfMeasure of string 49 | type CubeDimensions = { 50 | Height : Measurement 51 | Width : Measurement 52 | Depth : Measurement 53 | UnitOfMeasure : UnitOfMeasure 54 | } 55 | type MissingDimension = 56 | | Height 57 | | Width 58 | | Depth 59 | type UnexpectedFormat = { 60 | Message : string 61 | Line : int 62 | Column : int 63 | } 64 | type ParseErrors = 65 | | InputEmpty 66 | | UnexpectedFormat of UnexpectedFormat 67 | | MissingDimensions of MissingDimension list 68 | | UnitOfMeasureNotSupported of string 69 | type ParseCubeDimensions = string -> Result 70 | ``` 71 | 72 | Once you've got to this point, it's a great place to stop and review the design with another member of the team. Ideally the review may be as simple as sharing the code you have, depending on how much the types encapsulate the business logic and how much needs explaining. 73 | 74 | As processes become more complex you'll find that writing out what you intend to implement, before implementing it, can give you better insight into what you are about to build, and how you should build it. -------------------------------------------------------------------------------- /Lessons/day-04-pipe-operator.md: -------------------------------------------------------------------------------- 1 | # Day 4: Pipe Operator 2 | 3 | Before I start on what it is, let's discuss a problem it solves. 4 | 5 | We have the following calculation, `2 + 3 * 5`, and we'd like to represent that in code. Let's define a few C# functions to help do that. 6 | 7 | ```csharp 8 | int Add(int x, int y) => x + y 9 | int Multiply(int m, int z) => m * z 10 | ``` 11 | 12 | As we all know from school mathematics, we have to apply the multiplication first, and addition last. 13 | 14 | ```csharp 15 | var result = Add(2, Multiply(3, 5)); // Output: 17 16 | ``` 17 | 18 | While it is correct, it's not as easy to read, because as you scan left to right you are seeing that you need to Add something, but then you can't add that until you have got the output of the multiplication. 19 | In our heads we'd do the multiplication first, but we can't do that first unless we got the result first and passed that into `Add`. Like so: 20 | 21 | ```csharp 22 | var multiplyResult = Multiply(3, 5); 23 | var result = Add(2, multiplyResult); // Output: 17 24 | ``` 25 | 26 | Not too bad right now, but if we added more inputs to our calculation, the more verbose our code becomes. How can we solve that in C#? Extension methods! 27 | 28 | ```csharp 29 | static int Add(this int x, int y) => x + y 30 | static int Multiply(this int z, int m) => z * m 31 | ``` 32 | 33 | Now we can write our code as such: 34 | 35 | ```csharp 36 | var result = 3.Multiply(5).Add(2); // Output: 17 37 | ``` 38 | 39 | That feels better to read, we're now able to chain the result from the first function into the second function. It certainly reads better. 40 | 41 | So how does F# deal with this problem? You guessed it, the pipe operator ```|>``` 42 | 43 | The pipe operator is an operator that receives an input value and a function, finally returning the value of the function provided, the signature of it looks like this: 44 | 45 | ```fsharp 46 | a' -> (a' -> b') -> b' 47 | ``` 48 | 49 | I know the above looks daunting, but the apostrophe denotes a generic type, which in F# normally start from a and work up through the alphabet. You may have also spotted the brackets, those denote a function. 50 | 51 | In C# terms it would look like this: 52 | 53 | ```csharp 54 | Func, R1> 55 | ``` 56 | 57 | If you don't quite get the above, don't worry, signatures can take a bit of time to get used to reading, we'll cover those more over time. 58 | 59 | The short of it is that the pipe operator only takes in one input, a mapping function and outputs one value. 60 | Now for some F#. Let's create two functions to do the mathematics 61 | 62 | ```fsharp 63 | let add x y = x + y 64 | let multiply m z = m * z 65 | ``` 66 | 67 | As mentioned above though, the pipe operator receives one input and gives one output. How can we change our functions so they only take one input and one output? 68 | Currying, using our Curried Function 69 | 70 | ```fsharp 71 | let add2 = add 2 72 | let multiplyBy5 = multiply 5 73 | ``` 74 | 75 | Which means we can now use the pipes: 76 | 77 | ```fsharp 78 | let result = 3 |> multiplyBy5 |> add2 // Output: 17 79 | ``` 80 | 81 | Great, but we've sort of run into the same problem we had before in C#, because we were having to declare some variables before then pulling everything together for the final result. 82 | 83 | Luckily for us we don't need to declare those curried functions before the pipe operator, we can define them inline and it will work exactly the same 84 | 85 | ```fsharp 86 | let add x y = x + y 87 | let multiply m z = m * z 88 | let result = 3 |> multiply 5 |> add 2 // Output: 17 89 | printf "%d" result 90 | ``` 91 | 92 | And that is the Pipe Operator, it helps you to compose your functions into chains of functions, just like Extension methods, but without the need to explicitly create a static function using the "this" keyword. There are other benefits too, but I'll leave those for now. 93 | 94 | [Try today's code](https://try.fsharp.org/#?code=DYUwLgBAhgJjEA8IE8IF5EQNQoLAChRIBbAV2DAEsAHYVYiAL3QgYComCCiIAnEAM7lIGAMwQAPgD5WwmnQgBWSTNjwATBAD0WiAHlSYaoYBcEAIwB2Lvmq9KAOzAAzCACIApDDd9BwoA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) -------------------------------------------------------------------------------- /Lessons/day-18-pattern-matching-part-3.md: -------------------------------------------------------------------------------- 1 | # Day 18: Pattern Matching (part 3) 2 | 3 | The first two parts of pattern matching we touched on a number of different patterns, so to help remind you of what they are and what the syntax is you may find this slide from [Paul Blasucci's](https://twitter.com/pblasucci) F# Online talk on [A Deep Dive into Active Patterns *](https://www.youtube.com/watch?v=Q5KO-UDx5eA) a useful reference: 4 | 5 | _* Don't watch this video yet, keep it in your back pocket once you have learned and used active patterns a little, at that point you may find it interesting to see how others might be using them._ 6 | 7 | ![Pattern Matching Patterns](assets/pattern-matching-patterns.png) 8 | 9 | We covered a number of these patterns already, some we haven't. Feel free to google and explore any that I've not explained, hopefully they are fairly self explanatory though. 10 | 11 | I mentioned Active Patterns above, which may have you thinking what are Active Patterns? It's a feature that I thought was cool when first learning about it, but didn't quite understand where it would be useful. My mind has since been changed, and I realise how useful it is. Let's take a look at Active Patterns and what problem they help to solve. 12 | 13 | Active Patterns are functions that give you the ability to encapsulate some pattern matching logic inside it's own function, and use that function when pattern matching. 14 | 15 | That is an important thing to remember as we look at the below, it that an active pattern is a function. There are four types of Active Patterns, here are their names and their functional signature, as taken from Paul's talk: 16 | 17 | ``` 18 | Single-case - 'T -> 'A 19 | Multiple-case - 'T -> Choice<'A, 'B, ... N'> 20 | Single-case partial - 'T -> Option<'A> 21 | Parameterised partial - 'T -> P1 -> ... -> Option<'A> 22 | ``` 23 | 24 | There is a new type above you've not seen before, but I imagine you can work out what it does. A Choice type is similar to a discriminated union. Out of X number of types, the value returned can only be one of those types. I won't dwell on this, if you relate it to the way a discriminated union works then you should be fine. 25 | 26 | Let's break down each type of Active Pattern, what it does and where it can be useful. 27 | 28 | Single-case active pattern is essentially a map function. It's useful when you have a type that can always be converted to another. 29 | 30 | The signature as you just saw is: ```'T -> 'A``` 31 | 32 | Let's write a useless implementation of a function to help explain the usage: 33 | 34 | ```fsharp 35 | let (|NumberAsString|) (input : int) = input.ToString() 36 | let outcome = 37 | match 2 with 38 | | NumberAsString str -> str 39 | printf "%s" outcome 40 | ``` 41 | [Try the code](https://try.fsharp.org/#?code=LAKANgpgLgBAFAHwHIFcC2AjCAnAggZwGUpsBLAOwHMEBKeCgBxVgC4YKo6Bed8pqAHQAVAPbEyVODVChIsEcwDGItBBhdQMLTDQBDKIoAWMAEwwA7qSiHN2hDFSYcBcRUox8JGAFoAfB5IZEAYJKAAzGAAiAFJ8SJgFAxUIIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 42 | 43 | You'll notice that in our pattern matching we only need to provide one case, the F# compiler knows that there are no other alternatives to be handled. 44 | 45 | The above is fairly over the top considering we are matching on a single outcome, in this case we could speed things up by doing some inline pattern matching in the function 46 | 47 | ```fsharp 48 | let printAsString (NumberAsString str) = printf "%s" str 49 | printAsString 1 // Notice how it still expects an int 50 | ``` 51 | [Try the code](https://try.fsharp.org/#?code=LAKANgpgLgBAFAHwHIFcC2AjCAnAggZwGUpsBLAOwHMEBKeCgBxVgC4YKo6Bed8pqAHQAVAPbEyVODVChIsBhKgFxFSvFSYcykqpj4S3GAo4AzGACIApPnN6SMkMfJKiOqjACMQA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 52 | 53 | As you can see the `printAsString` input still expects an integer, the inline pattern matching helps us to avoid the need to do pattern matching lower down in the function call. 54 | 55 | If we wanted access to both the original value and the pattern matched value, we can wrap that inline pattern matching with "`as [variableName]`" and get access to both: 56 | 57 | ```fsharp 58 | let printWithBoth ((NumberAsString str) as number) = 59 | printf "Original value %d and the matched value: %s" number str 60 | printWithBoth 1 61 | ``` 62 | [Try the code](https://try.fsharp.org/#?code=LAKANgpgLgBAFAHwHIFcC2AjCAnAggZwGUpsBLAOwHMEBKeCgBxVgC4YKo6Bed8pqAHQAVAPbEyVODVChIsBhKgB1UlAAWAIRHr4cVJhwFxFSjHwk6AQ3wxy6LNm6gYLmAo4AzGACIA8mUoKSzAYADdglAgYAFIAExhLcnj1KLRLKABjNQh48LBItmj8b1t7HDMSGRB3cmVVTW01GABGIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 63 | 64 | The above is obviously a pointless implementation, I wish I could give a more useful example than the above but I'm at a loss right now. Personally I've not used a Single-case pattern in any of my F# yet, one day soon I'm sure it will come. 65 | 66 | You can think of it as very similar to a Single Case Union, as there is only one outcome available to you. -------------------------------------------------------------------------------- /Lessons/day-05-everything-is-an-expression.md: -------------------------------------------------------------------------------- 1 | # Day 5: Everything is an expression in F# 2 | 3 | Let's define an expression out loud so that everyone is clear. An expression is a combination of values and functions to create a new value, i.e. your code will always return something. 4 | We can obviously write expressions in C#, we've done many in this channel over the last 5 days. Just roll out the lambda syntax, avoid the braces and away you go expression ahoy: 5 | 6 | ```csharp 7 | Func concat = (a, b) => a + b 8 | ``` 9 | 10 | What C# also lets you write is Statements. A statement is when you have code that will execute but return nothing. Notably a `MethodGroup` returning `void`, or an `Action`. 11 | 12 | ```csharp 13 | Action log => msg => Console.WriteLine(msg) 14 | ``` 15 | 16 | The question is, if F# only allows you to write expressions, how do you write a function that would be void in C#? 17 | Answer: It doesn't, because everything is an expression and therefore it always returns something. I'm sure there is some deeper meaningful reason in the mathematics that F# is built upon as to why this is, but in laymen's terms it comes down to compossibility. 18 | 19 | How can you chain two functions together that return nothing in an expression? You can't, it's impossible. So what do we do? The answer is `Unit`. 20 | 21 | `Unit` acts as a placeholder when no other value exists or is needed. 22 | 23 | In fact each of us has used a Unit at some point already in our C# code because that is a type that Mediatr uses when you have execute a command that returns no value. While it may display this to us as a handler that returns void, under the hood that then defers to a handler that returns a Unit. The reason they did this was to simplify their own codebase. 24 | 25 | Note that Unit does not exist in the BCL or C#, it is a F# type. Mediator implemented their own Unit-like type. 26 | 27 | Here is the equivalent to the above C# code, we have to explicitly return `unit`, which in F# is represented in code as open and close brackets: 28 | 29 | ```fsharp 30 | let log msg = printf "%s" msg ; () // string -> unit 31 | log "Hey there" 32 | ``` 33 | 34 | Great, so we know that a function must have an output, and the lowest common denominator for that in F# is a `unit`. 35 | 36 | The next question is how can you compose something that has zero parameters. For example: 37 | 38 | ```fsharp 39 | let doNothing() = () 40 | ``` 41 | 42 | You may be able to guess once I give you the signature for the above unit -> unit 43 | As you can see, we have an input after all! It's our good friend Unit. Great news, now we have an input and an output we can compose two functions easily. 44 | 45 | ```fsharp 46 | let doNothing() = printf "Running doNothing" ; () // unit -> unit 47 | let result = doNothing() // Run by itself 48 | printf "Finished" 49 | let chainedResult = () |> doNothing |> doNothing |> doNothing // unit -> unit 50 | printf "Finished" 51 | ``` 52 | 53 | So now we know that all functions must have one or more inputs, and that they must have one output. Which means that every function in F# is composable, as long as output of one function maps to the input of the next function, they can be composed. 54 | 55 | On top of this, with the above knowledge we now understand function signatures to their fullest (I think!). 56 | If you want to know the output of a function, look at the last item in the list. 57 | If you want to know the inputs, look at each item before the last item. 58 | The more you see function signatures, especially of functions you write yourself, it becomes easier and easier to read them. 59 | 60 | Nesting of function signatures in function signatures can become a little confusing, but that is where you should pay close attention to the brackets that denote the start and end of the function definition. It can remind me of some of the more confusing generic type signatures in C#. 61 | 62 | | Input | Output | Signature | 63 | | :------------------------------------------------------: | :--------------------------------------: | :--------------------------: | 64 | | unit | unit | `unit -> unit` | 65 | | string | unit | `string -> unit` | 66 | | Tuple of int & string | string | `int * string -> string` | 67 | | int, Function accepting int and returning int | int | `int -> (int -> int) -> int` | 68 | | int | Function accepting int and returning int | `int -> (int -> int)` | 69 | | Generics- two objects of type `a'` as separate arguments | `List` | `a' -> a' -> List` | 70 | 71 | [Try today's code](https://try.fsharp.org/#?code=DYUwLgBAJg9gcjMALAlgOwOYAoCUEC8AsAFARkQAOATumAGYQBEASgK5probTyKqaMS5CLhIlQkKiADOrYJHw8EyLqOIlqtBowBi6FNKQgog9cQkQAxkgCG6Y8xlyFIvAB8AfEr5cIn7yqYfl6wyvwYYsSaaPRMepyGxoxAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) -------------------------------------------------------------------------------- /Lessons/day-06-fsharp-syntax-and-more-functions.md: -------------------------------------------------------------------------------- 1 | # Day 6: F# Syntax and More Functions 2 | 3 | In Day 5 I explicitly avoided doing any multi-lined functions because I wanted to avoid the discussion around F# syntax. Specifically F# syntax for methods and scopes. 4 | 5 | As you've probably noticed by now, we aren't wrapping every function in curly braces, nor are we adding semi-colons at the end of every line. 6 | 7 | F# uses whitespace indentation to know what lines of code belong to that function. If the function is on one line, that's simple enough and requires no explanation, as we've seen before now. 8 | 9 | Multi-line functions, or even variable assignments must start the definition on the first line, but the content of that assignment / function must start indented on the next line, with all other lines of that function following after with the same indentation. 10 | For example: 11 | 12 | ```fsharp 13 | let add x y = x + y 14 | let multiply m z = m * z 15 | let oneLineResult = 3 |> multiply 5 |> add 2 // All on one line like before, no problem 16 | let multiLineResult = 17 | 3 // Multiple line expression requires you to start on the next line 18 | |> multiply 5 // The number of spaces you use is not important 19 | |> add 2 // But the indentation must remain consistent 20 | ``` 21 | [Try code](https://try.fsharp.org/#?code=DYUwLgBAhgJjEA8IE8IF5EQNQoLAChRIBbAV2DAEsAHYVYiAL3QgYComCCiIB7AOxAAZSoIBKIAM7lIGAMwQAPgD5WMmnQgBWJatjwATBAD0xiAEFgwPvxsgIwUSAA0ESbwj8P1AE68ARqDE3OBqFJQi4lIy6AQQ8RAKphAAsuq09o6CECAIvlKSlAIQPiAAjqSUpZIovKQQYB6SYFA+kMVgABb2ggiQWSBxCSphVLSoOskAKt2epMT+ID58AGZu1FAAxlK19aSS9pQ1XpCUxNS8bVD8YEPxI-oQRskAQqSQXSD0+5ClxFCiCCbASFZogG4EIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 22 | 23 | As I said, functions and assignments have the same syntactic rules, so if I wrote my doNothing function from Day 5 as multi-line, it would look like so: 24 | 25 | ```fsharp 26 | // Originally 27 | let doNothingOneLine() = printf "Running doNothing" ; () 28 | // New version 29 | let doNothingMultiLine() = 30 | printf "Running doNothing" 31 | () 32 | ``` 33 | [Try code](https://try.fsharp.org/#?code=PTAEHkCcEsHNoHYEMA2KCeBYAUCgpgC6gAmA9gHKkEAWis4CeAMongBQCUoAvKAA4wEBAGagARACUArggR0SFKrQSwxoANyhOOHCFDk8Ad1AA3PJADO0Ughz4iZSjToBZKSgLQWjTjxygA-kERcWlZeUclOjF-QM4gA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 34 | 35 | You'll probably notice in both functions above I've not used the keyword return at all. That's not to say that the return keyword doesn't exist in the F# language at all, because it does, BUT for expressions it is not required. The reason for that is because we are in an expression and you cannot exit an expression early. The same as true for C#. 36 | 37 | In C#, you can't do anything except return a value, however in F# you can define values in the function scope and use them later. 38 | 39 | Remember that functions are first class citizens, which means you can define functions too. You may wonder how it it differentiates between one functions implementation, and the parent function? Indentation. 40 | 41 | ```fsharp 42 | let toUpper value = 43 | let upperCaseChar c = 44 | System.Char.ToUpper(c) 45 | let someArbritraryValue = "not needed, but demonstrating" 46 | String.map upperCaseChar value 47 | printf "Stop shouting %s" (toUpper "will ward") 48 | ``` 49 | [Try Code](https://try.fsharp.org/#?code=DYUwLgBGD2CqAO8QCcIDcCGwCuIIF4BYAKAjIlEm0RQGEMBnEWgCw1QGMCTzeIBlAJ4MwIALYA6VuwkAVODWQAKDgEoe5ShAbQxIAILIARsgCWYZO0EA1LLgIQARADtokZyBAATbwBoIRtiQPmLQziKWYKbOAOaOGmT8FtExEmIY8BDUSMj0TNKomDggJCTwZs5gAGZOSdCZDCzQQSkQAKQMjhBKMAg5TgDupsDAEAPsXo6qQA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 50 | 51 | Because we're always expecting an ouput from a function, if we aren't using that value then there's probably something we've done wrong. 52 | 53 | F# will warn you when this happens. Try and modify the above code like so: 54 | 55 | ```fsharp 56 | let toUpper value = 57 | let upperCaseChar c = 58 | System.Char.ToUpper(c) 59 | let someArbritraryValue = "not needed, but demonstrating" 60 | String.map upperCaseChar value // You'll see a warning on this line 61 | String.map upperCaseChar value 62 | printf "Stop shouting %s" (toUpper "will ward") 63 | ``` 64 | 65 | You can solve this two ways. The first would be to use let and assign it to a variable, which is probably what you want most of the time. Sometimes you don't need the functions output, and in those cases you can use the ignore function, to discard the value and remove the compiler error. Modify the appropriate line as such: 66 | 67 | ```fsharp 68 | String.map upperCaseChar value |> ignore 69 | ``` 70 | 71 | Learning all of the above was a bit of a game changer for me. Not because it does anything spectacular, but because it was unspectacular. I'd tried to implement a more functional approach to programming in C# using the `Language.Ext` library and Query Expressions but it wasn't easy. So my imagination led me to believe that functional programming on the whole was hard, but really it's mostly different. Once you learn those differences there's a whole new world that opens up. One with a lot less boiler plate, and with a compiler that tries to do the heavy lifting for you. 72 | -------------------------------------------------------------------------------- /Lessons/day-11-single-case-unions.md: -------------------------------------------------------------------------------- 1 | # Day 11: Single Case Unions 2 | 3 | In our example of Address last week we had a type with each string representing different parts of it: 4 | 5 | ```fsharp 6 | type Address = { 7 | Line1 : string 8 | City : string 9 | PostCode : string 10 | Country : string 11 | } 12 | ``` 13 | 14 | As a type gets larger it's fairly easy to create bugs in our code by assigning the wrong variable to the wrong property, they are both strings after all. It's easily done, often when copying and pasting. Another common scenario is when accepting a Entity Id as a function input, or even easier to get wrong is a boolean in a signature call. Make the wrong tweak and you could have the wrong boolean being assigned to the wrong argument. 15 | 16 | Single case unions to the rescue! 17 | 18 | The syntax is very similar to a discriminated Union, however instead of using a pipe character to separate the OR options, you only give it one. As follows: 19 | 20 | ```fsharp 21 | type Country = Country of string 22 | ``` 23 | 24 | They act in a very similar way to discriminated unions too because the label once again acts as a wrapper for your data. Like so: 25 | 26 | ```fsharp 27 | let england = Country "England" 28 | ``` 29 | 30 | To get the value out again can be done in a few ways. 31 | 32 | ```fsharp 33 | let (Country englandStr) = england 34 | printf "%A" england 35 | printf "%s" englandStr 36 | ``` 37 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAwg9gVwHbAE4igXlol6pwBmUAzmgJZIDmAsAFD0A2EwUE1jAhkgCZY7I0GAEQBRDtx7D6TFlAAU8QfnZUuvAMpoAlP1XqeMumFSVgxYQFIAgsLYTe9E2YuWSd-ZK2ogA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 38 | 39 | We could create a module to help define a function to unwrap it for us, which can be cleaner at times 40 | 41 | ```fsharp 42 | module Country = 43 | let value (Country c) = c 44 | let england = Country "England" 45 | printf "%s" (Country.value england) 46 | ``` 47 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAwg9gVwHbAE4igXlol6pwBmUAzmgJZIDmAsAFD0C2cAJggDbTzJoab1RBUTsCgA3AIbsE0ABTc8GAMYBKLFCX16IqBGrsJSFuoW8oAIgCi+wy3Na6YVJWDFzAUhLmo83LwB0ktLQelQGRipAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 48 | 49 | You can also use pattern matching too, but that's a bit contrived for a simple scenario like this. 50 | 51 | As with discriminated unions you cannot compare a single union case with a variable of it's inner data, you would have to wrap or unwrap it first 52 | 53 | ```fsharp 54 | let england = Country "England" 55 | printf "%b" (england = "England") // Compiler Error 56 | let englandStr = Country.value england 57 | printf "%b" (englandStr = "England") 58 | ``` 59 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAwg9gVwHbAE4igXlol6pwBmUAzmgJZIDmAsAFD0C2cAJggDbTzJoab1RBUTsCgA3AIbsE0ABTc8GAMYBKLFCX16IqBGrsJSFuoW8oAIgCi+wy3P0wqSsGLmApACNzUWXqoGjdSsbI3M1AHpwnEYwck5UKEtUVDhULTodPwCWAGU0E1xeADpJaWgs2wcnFFdPb18Q3PzsYP9bMKA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 60 | 61 | It goes without saying that equality is structural once more: 62 | 63 | ```fsharp 64 | let england = Country "England" 65 | let england2 = Country "England" 66 | printf "%b" (england = england2) 67 | ``` 68 | 69 | Let's update our Address example to use single case unions: 70 | 71 | ```fsharp 72 | type Line1 = Line1 of string 73 | type City = City of string 74 | type PostCode = PostCode of string 75 | type Country = Country of string 76 | 77 | type Address = { 78 | Line1 : Line1 79 | City : City 80 | PostCode : PostCode 81 | Country : Country 82 | } 83 | 84 | // When creating the address we must pass the string value into our SU 85 | // Feel free to play around with swapping the SUs to see a compilation error 86 | let address = { 87 | Line1 = Line1 "221b Baker St" 88 | City = City "London" 89 | PostCode = PostCode "NW1 6XE" 90 | Country = Country "UK" 91 | } 92 | 93 | let newAddress = { 94 | address with 95 | City = address.Country // Won't compile because City is not compat with Country 96 | } 97 | ``` 98 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAMglgOwgRigXlolUD2AzKAZ2ACdEBzAWAChRIoBhOUdR5kXA4shK28aAAUcxBjgAm0DMNETo+IqQo060MQFcEpDhg1aSHBd2XUVAqAEFx4khEKFWAbxpRXmJKgBc7lC7dMWbwCQP1cZYDFJKG9wyIhQxhxNbWjE5IMaAF8aGgB6XKgAdQALCAQoAGNbAENgCihgUqhq61t7KAB3aABbdWIoMGr2xuhjXigAN2qAG3VoRGAcXHUSKABlAFU8goAxCAhpqDxbaEWB6eqOapIkhHFO5mKiDuqwMHqR9Y2HM8J95sqOG67wudRw5QgJBuJBo0wgwGarTsDgwzmobh8qAw8A8UAARAAmAnIABGUAAQtUANaQ9bAPEJYKsJl4mDg8TghnotyxOSsXlRPEAOUKqAAbAANACiXIxehSuluKTxGwA0lzsqZqHCEUgOlYbMinAkWob2h1HlAEnL2KxTW1CAA6eUGKD5IrggDkCIqQJB0BJEAq1T6altcAcCBwPr9tQejTS+hC3NcmSAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 99 | 100 | This may feel over the top, but it is so quick and easy to do that it has few downsides. You can use it at your own discretion. If you do though, you've now made it far less likely that you will put the wrong data in the wrong place. 101 | 102 | In fact this very simple step can remove the need for a number of unit tests that you may write to ensure the right properties are being placed in the correct fields or passed in as the correct arguments. The fact is that any unit test that does this is often a waste of developer time. 103 | 104 | We're not done with SU yet though, we have some more ground to cover with them tomorrow. -------------------------------------------------------------------------------- /Lessons/day-16-lists-part-1.md: -------------------------------------------------------------------------------- 1 | # Day 16: Lists (part 1) 2 | 3 | Today I want to talk to you about lists, specifically the F# list implementation. This is not your BCL's list implementation, of course that couldn't be the case because the List type from the BCL is mutable. 4 | 5 | F# has implemented it's own version of a list using a linked list structure and most functional languages would use a similar approach. It's worth learning how the linked list is implemented so that when we interact with a list you understand why it does it in that way. 6 | 7 | A linked list is a recursive type, you can think of it similar to the following type: 8 | 9 | ```fsharp 10 | type List<'T> = 11 | | Empty 12 | | Cons of 'T * List<'T> 13 | ``` 14 | 15 | In the above I've implemented it as a discriminated union, where by it is either Emtpy, or it is Cons. Cons holds an item of T and a list of T, it does this as a tuple. You'll hear the term Cons frequently, so it's worth remembering that term and it's relation to a list. 16 | 17 | Let's create a small list of items using the above implementation: 18 | 19 | ```fsharp 20 | let emptyList = Empty 21 | let paul = Cons ("paul", Empty) 22 | let peterAndPaul = Cons ("peter", paul) 23 | ``` 24 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAMglgZ2AHgOQBUB8UC8BYAKCmKgB8oBRAWzFEJLKgGEB7AOwShYDMoMoAVLEQoMmQoQA2EYFAg1Q8JLkoKQUmVDABDAK6SVrDlAAUAIh36zAGlW0QASg2zIwCACcAgmwAmABT0DHGZ2TnNXDxstQKcCQjB3ODZgXjMAUk8zOTUlYHjE5NSMrMtJfKSUqHTMrRkPb39AoA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 25 | 26 | If you look carefully above, you'll notice that we don't append an item to the list, it is prepended. Why would we do that? Immutability is the answer. We can't modify the last Cons to add our new item, because it is immutable. What we do instead is wrap the existing list inside a new Cons, this gives us a new list, with the additional item prepended. 27 | 28 | The beauty of the above is that we haven't modified anything, it's a very efficient way of expanding a data structure. We continue to use the original list underneath, and thus only add a small amount of memory. 29 | 30 | If you want to add an item into the middle of the list, F# be able to re-use some of the list, but the other half would need to be created from scratch: 31 | 32 | ```fsharp 33 | let emptyList = Empty 34 | let paul = Cons ("paul", Empty) 35 | let peterAndPaul = Cons ("peter", paul) 36 | let johnBetweenPeterAndPaul = Cons ("peter", Cons ("john", paul)) 37 | ``` 38 | 39 | Above you'll see I have to create a new Cons for peter, as well as john, but I can re-use paul. This is why you would avoid inserting items into the middle of a list in F#, because it creates more allocations than prepending. It's not that you can't, but there is probably a better tool for the job, such as the BCL implentation, which is called `ResizeArray` in F#. 40 | 41 | Creating a list in F# is very simple, you will have seen it before now but not necessarily realised it is a list. Let's create a list and prepended an item to it using the Cons (```::```) operator (see, I told you we'd mention Cons) 42 | 43 | ```fsharp 44 | let emptyList = [] 45 | let paul = ["paul"] 46 | let peterAndPaul = "peter" :: paul 47 | let altPeterandPaul = List.Cons ("peter", paul) // equivalent to the above 48 | ``` 49 | [Try the code](https://try.fsharp.org/#?code=LAKANgpgLgBBC2AHKBPAMgSwM6wLwwG0BdUSWRAQwFcwZ8CAiSmhk8aGRaCAJwEEAdgBMACtVr4m3HgxgAuOZ3GkOFMFBHSKwsTToxMOAHQBhAPYCsMABRSovBgBolNAJQwA9B7gBHKhgA3NQgBWCgzGCgACwgYCgAjMwCIUFBEHgxQgDMYBgBSPllmMDSM7NyCoulBUWUQdMyoHPzCuPVNex5tWpogA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 50 | 51 | The cons operator is built fsharp core and gives us a nice clean syntax. We use it in other scenarios too, but we'll get to that tomorrow. 52 | 53 | So how do we interact with this list, other than prepending to it? Well basic list operations such as Map (Select), Filter (Where), Fold & Reduce (Aggregate) are all available on the List module. 54 | 55 | They act in the same way as the C# equivalent extension method, give it the function you wish to map / filter / fold / reduce with, and then the list itself. With pipe it isn't drastically different either: 56 | 57 | ```fsharp 58 | let newResult = 59 | names 60 | |> List.filter (fun str -> str.EndsWith("l")) 61 | |> List.map (fun str -> str.ToUpper()) 62 | ``` 63 | [Try the code](https://try.fsharp.org/#?code=LAKANgpgLgBAdgQwLYQM4wLwwNowEQAO0EATnjANz4EICuY5AuqKJLHBAO4BKa9sGUDGHxkaISIA+APhgAZAJaooAOgBmCsFFIwAFGtpwYykjAC0skyoCicACaoA6gqgALXXgYBKLxOEz5JVUkBAI9AyMTc0soEhUAFQB7AFUCIhJdHxYQAhIFOCg1fABSAEFyDh4+LSA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 64 | 65 | If you played around with the code above you may wonder how to get a specific item out of the list. For example, how do you get the 3rd item? You can use slicing: 66 | 67 | ```fsharp 68 | let names = ["john";"peter";"paul";"don";"phillip"] 69 | printf "%A" names.[0] // Index 0 70 | printf "%A" names.[1..2] // Index 1 through to 2 71 | printf "%A" names.[..4] // Everything up until 4 72 | printf "%A" names.[4..] // Everything after 4 73 | printf "%A" names.[5..100] // No exception thrown if items not found 74 | ``` 75 | [Try the code](https://try.fsharp.org/#?code=LAKANgpgLgBAdgQwLYQM4wLwwNoCIBWA9gBZy4DcuADtBAE4XUICuYjAJoWZVcQJZgwfKrgC6oKnT5woAMxi4ApAEFc8ZGgB02AAyiYAegMwAknHYQAHjB0SpM+UtXqUqbQEZNmgEz6jp8ysYdxgoYjpCZgBzYlDCGG8YO2k5BRU1RFdtLwAWP2MAUQA3egBPMOkomGYqapkBGBzkhzTnTK1sHK98mGKyirgqhFkoekbm1KcMjTdsAFYvdx09Q2MAOXirAGMIKig+LlDwwgB3OBg+eT5RpHQ4QlhZSPMgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 76 | 77 | That's enough for today, but we'll cover some more list functionality tomorrow. -------------------------------------------------------------------------------- /Lessons/day-08-data-and-behaviour.md: -------------------------------------------------------------------------------- 1 | # Day 8: Data and behaviour 2 | 3 | In F# and functional programming in general, data and behaviour are kept separately. Bearing in mind it's built on top of the BCL it's not enforced, but it helps you to do this by default by using record types, discriminated unions and modules. 4 | 5 | We'll cover each item in turn, but today's focus will be on record types. Record types are a super powered equivalent of C# POCO objects. 6 | 7 | ```csharp 8 | public class Address 9 | { 10 | public string Line1 { get; set; } 11 | public string City { get; set; } 12 | public string PostCode { get; set; } 13 | public string Country { get; set; } 14 | } 15 | ``` 16 | 17 | Here is an F# Record Type equivalent: 18 | 19 | ```fsharp 20 | type Address = { 21 | Line1 : string 22 | City : string 23 | PostCode : string 24 | Country : string 25 | } 26 | ``` 27 | 28 | Looking at the two there doesn't seem like much difference, but looks can be deceiving. 29 | 30 | The C# POCO can be instantiated with as many or as few props as you want it to be, but with F# you must provide every single property of the record. You can of course do this with C#, but you will need to create the constructor yourself, or via resharper, and then make sure to keep it up to date. Not to mention avoid creating any bugs by applying the wrong argument to the wrong property, which I think we've all done in the past. F# generates the constructor for you, not that you interact with it in this way when creating records in F#. That's more for interoperability with the BCL. 31 | 32 | For example, to create an F# record you do it as so. 33 | 34 | ```fsharp 35 | let address = { 36 | Line1 = "221b Baker St" 37 | City = "London" 38 | PostCode = "NW1 6XE" 39 | Country = "UK" 40 | } 41 | ``` 42 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuKFYRSgcwOKgGEzR7HmbDsQAKAeyZcxcaAyYsK7QsSkBXCsxD95Q-AF8CBADYRgUAIYJkaTDmFFyVWlgBEAJjfUARlABC5gGsIRCgAZWAXe25eLVcSMQo4BMjlInFJaWhXADkAdVoANgANAFEUzjUNRFioFwBVAGkUvSA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 43 | 44 | When trying the example, if you put your mouse over the address variable you'll see that it is of type ```Address```. Again the compiler has done a bit of thinking for you, it knows that the only object available to you in the current scope with those properties is Address, it infers that's what you want and sets the type accordingly. If you edit the example to remove one of the properties you'll see it will warn you that you are missing a property, in this example you can remove all but one of the properties and it will still know what type you are trying to create, because no other type has a property of the same name. 45 | 46 | The next feature you get with Record Types is immutability. The compiler won't let you change any value on an instantiated object. Where as C# is mutable by default. You can create immutability in your C# POCO so the properties are read-only, only allowing them to be set by the constructor. If any of your other POCOs are mutable, then you're out of luck, your object can be changed from under your nose. For example if you used a List, it can be modified by any other calling code. You would need to use an immutable version of a list. Remember that F# lists and the List as we know it are not the same. The List type from the BCL list is called a `ResizeArray` in F#, which I think makes it pretty clear what is happening. 47 | 48 | Open the above example again and try to set a property on the address, you'll see the compiler won't let you. I've realised I've not been clear on the benefits of immutability before, if you think I should cover that, then let me know. 49 | 50 | Penultimate feature, structural equality, meaning that when comparing record types it checks every value in the object before deciding if they are equal or not. Meaning two different instances that have the same values will be considered to be equal to one another. 51 | 52 | ```fsharp 53 | let address = { 54 | Line1 = "221b Baker St" 55 | City = "London" 56 | PostCode = "NW1 6XE" 57 | Country = "UK" 58 | } 59 | let address2 = { 60 | Line1 = "221b Baker St" 61 | City = "London" 62 | PostCode = "NW1 6XE" 63 | Country = "UK" 64 | } 65 | 66 | let doAddressMatche = address = address2 67 | printf "%b" doAddressMatche 68 | ``` 69 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuKFYRSgcwOKgGEzR7HmbDsQAKAeyZcxcaAyYsK7QsSkBXCsxD95Q-AF8CBADYRgUAIYJkaTDmFFyVWlgBEAJjfUARlABC5gGsIRCgAZWAXe25eLVcSMQo4BMjlInFJaWhXADkAdVoANgANAFEUzjUNRFioFwBVAGkUg3wTM0skVBQ3WzxU0koaW3dPH38gkPDylRjh+MTkqPTgKRlhvMLS6aJKzWHG5sNW0ygk+E60AFlzYABjAAssiysu2w7rboIwBWAAM1qAKReFynMTnD7XO6PIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 70 | 71 | Note that we are using ```=``` to compare the values. How can that be? Remember data is immutable by default so you can safely use = to compare two objects. Setting a mutable value, which is possible, uses a different syntax which means you can never accidentally mutate a value using the ```=``` operator. 72 | 73 | Again you can do the above with C#, but now your code for this simple POCO has become a LOT more verbose due to all the hoops that you have to jump to with regards to HashCode etc, not to mention making sure you don't cause any bugs by missing properties or compare the wrong items. Hand rolled code for this is often going to have issues. 74 | 75 | Let's finish off with a problem that immutability brings, how can I store a new version of an address without needing create the whole object again? We use the with syntax: 76 | 77 | ```fsharp 78 | let nextDoor = { 79 | address with 80 | Line1 = "221a Baker Street" 81 | } 82 | ``` 83 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuKFYRSgcwOKgGEzR7HmbDsQAKAeyZcxcaAyYsK7QsSkBXCsxD95Q-AF8CBADYRgUAIYJkaTDmFFyVWlgBEAJjfUARlABC5gGsIRCgAZWAXe25eLVcSMQo4BMjlInFJaWhXADkAdVoANgANAFEUzjUNRFioFwBVAGkUg3xjUygqAA9gABExMRCsPFSLK1R0AHdeAAsozkcaW3dPcz9A4LDmCFNy4ha2syT4JHGAWXNgAGNprNGTmywu3v7EAjAFYAAzWoBSLxcoEcxmhzlcbkA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 84 | 85 | So there we have it, the Record Type. A very light syntax with a lot of benefits. There are more features available on a Record Type, such as being able to add methods to it. We can leave that for now though. 86 | 87 | A good way to think about a Record Type is that it is a group of properties that must be declared something. They are an AND of properties, you have to have them all, as Scott Wlaschin so beautifully put it in his book. -------------------------------------------------------------------------------- /Lessons/day-14-pattern-matching-part-1.md: -------------------------------------------------------------------------------- 1 | # Day 14: Pattern Matching (part 1) 2 | 3 | Put simply, pattern matching is if / else on steroids*. C# has had a form of pattern matching for a while, it was very limited at first. In C# 8.0 they gave it a boost in the arm, which you can [read about here](https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/may/csharp-8-0-pattern-matching-in-csharp-8-0) if you are unaware of the new functionality. 4 | 5 | \* You remember this reference? Top marks for those that do. 6 | 7 | You can use it to assign a value to a variable, or as the last step before returning a value from your expression. The syntax pattern matching is as simple as: 8 | 9 | ```fsharp 10 | match [variable] with 11 | | [matching pattern] -> [output] 12 | | [as many patterns as required] -> [output] 13 | ``` 14 | 15 | The key point here is to make sure that the branches of logic, start inline or indent further than the match x with syntax. The below example would throw a compiler error at you, try it and see what the compiler says. 16 | 17 | ```fsharp 18 | let isTrue bln = match bln with 19 | | true -> "Yes" 20 | | false -> "No" 21 | ``` 22 | 23 | Let's create a very simple function to show the above 24 | 25 | ```fsharp 26 | let isTrue bln = 27 | match bln with 28 | | true -> "Yes" 29 | | false -> "No" 30 | printf "%s" (isTrue true) 31 | printf "%s" (isTrue false) 32 | ``` 33 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAlgzgKgJwK4ggI2AOwgXgLABQExEAtgIZgDGAFulhAO5Rg2EkQA+EYyqAtAD4IAIgCaIGCPYluAM3LAYA4SIByAe2kFCABwRRMYOaICkUiAApYiFDz4BKPQaMmR5kVZt8ICpSAcgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 34 | 35 | The above is called the Constant pattern, because we are matching the value of the variable against a constant compiled into our branches of logic. 36 | 37 | Another useful pattern is called the identifier pattern. You can associate this with your understanding of discriminated unions, where by you state the label of what you are trying to match. 38 | 39 | We can do this for an `Option`, which is a discriminated union of 40 | 41 | ```fsharp 42 | // Some | None, like so: 43 | let isOptionTrue bln = 44 | match bln with 45 | | None -> "Nothing provided" 46 | | Some true -> "Yes" 47 | | Some false -> "No" 48 | 49 | printf "%s" (isOptionTrue None) 50 | printf "%s" (isOptionTrue (Some true)) 51 | printf "%s" (isOptionTrue (Some false)) 52 | ``` 53 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAlgzg8gBzFA9gOwCoCcCuIIBGwaEAvALABQENEAtgIZgDGAFocRAO5RitW0IAHwgA5dPgC0APggAicXyhoA5hARYUANygATELrkDaIgMoo6+MLimy5ATRAwj1ExHOWIAMwbAYt+XEXKg1lMC95AFJnCAAKWERkdGw8MQkAShCsMIi5aLk4hKRUTBs4jysbdMzKULRwqJj4+GLkstiK719-aqA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 54 | 55 | Or to prove the point that it will work for any discriminated union we'll use one of our earlier examples: 56 | 57 | ```fsharp 58 | type Address = { 59 | Line1 : string 60 | Road : string 61 | City : string 62 | PostCode : string 63 | Country : string 64 | } 65 | 66 | type PreferredContactMethod = 67 | | Email of string 68 | | Mobile of string 69 | | Letter of Address 70 | 71 | let sendMessageToCustomer contactMethod = 72 | match contactMethod with 73 | | Email email -> printf "Sending an email to: %s" email 74 | | Mobile mobNumber -> printf "Sending text to: %s" mobNumber 75 | | Letter address -> printf "Sending letter to: %s %s, %s" address.Line1 address.Road address.PostCode 76 | 77 | sendMessageToCustomer (Email "john@fsharp-city.com") 78 | sendMessageToCustomer (Mobile "0800-fsharp") 79 | sendMessageToCustomer (Letter { 80 | Line1 = "15" 81 | Road = "The High Street" 82 | City = "city" 83 | PostCode = "N1" 84 | Country = "England" 85 | }) 86 | ``` 87 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuKFYRSgcwOKgCUB7AQzj1GzNh2IBhMqCFMWFdoWIAFHk3E840BrNGKi6gK4VmIGSPkEAvgVCQoS5ADMIiZHHXG+AY2ABZCMAAFhqYYkQAPlAAogC2fGQANlA8jsJyCpyRvjwARonQKWm6maQBwC7JqfBIqCgEBAkBjBAUcP5ofKwQACo84gZMPDEVXjyePv5BIRhhUHHAXoFQo+N+AcGCAO5SgbORsfFJEHGJUAC0AHxQYHLAqQBEAMotcGxQfBRQx4dQwDwMAFIUPcvicEnsoNk8o05rkAHIGGI5CqXa63B7PVpvcoAD2Av3+UCBIJi8MRyMQEJIZQqAhqaHOVxulDuUCeLzejWA5UQBMB6CBABoicD3ghkGgAHTkKi0OkSlCS3gCMX0xUqNQaCD1fAoF7tFCdHp9AZ-Ya8gAUB1O9wAVjxAhQAAKOFCBPiIMBnLxSECS0Yxe4ASgIetaBqNvX6g3NUAtUPybIADAAOJNJs6u92e4Oh-W1SMmmMVC3U7kVPB6TikSg0TBs6gAVnus04ysEWHu3UC0AAEmRWEtHswIAEW1XOJJpJ2faBx9XlKpgOpNPX7nDqPOF1BDMZEKZO1F5AkPnAt0RLEGgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 88 | 89 | The one thing I've not told you about pattern matching is that it must be exhaustive. The same is true in C# too, except that in C# it cheats by making you add a default / discard branch. In F# it can often (not always) work out whether you have covered all options. Let's say the business decides we should be able to contact our users via Skype, now you head back into your code to add the Skype option and try to run the code: 90 | 91 | ```fsharp 92 | type PreferredContactMethod = 93 | | Email of string 94 | | Mobile of string 95 | | Letter of Address 96 | | Skype of string 97 | ``` 98 | 99 | You should notice a compiler error telling you that you've not handled the Skype label. Being a good developer you would add that option, and handle the scenario as required. 100 | 101 | Sometimes it's possible that you only care about certain scenarios, in those cases it may be tempting to use wildcard pattern and simply ignore anything you've not handled like so: 102 | 103 | ```fsharp 104 | let sendMessageToCustomer contactMethod = 105 | match contactMethod with 106 | | Email email -> printf "Sending an email to: %s" email 107 | | Mobile mobNumber -> printf "Sending text to: %s" mobNumber 108 | | Letter address -> printf "Sending letter to: %s %s, %s" address.Line1 address.Road address.PostCode 109 | | _ -> printf "Don't worry about it" 110 | ``` 111 | 112 | The problem with doing this is that you have now lost the exhaustive pattern matching available to you. If we added another contact method to our list, it would fall into the above and wouldn't warn you that you should handle that. Use the wildcard safely. 113 | 114 | There we go, a quick insight into pattern matching. You may be surprises by the concept of the above being a "quick insight" into pattern matching, but it is true. There are many patterns that can be used in pattern matching, and we'll cover more of next time. The above is good enough for today though, and hopefully let the idea settle into your mind of how it can be useful, especially when you are at the end of an expression and you have different branches of logic that you need to apply to provide the output. -------------------------------------------------------------------------------- /Lessons/day-15-pattern-matching-part-2.md: -------------------------------------------------------------------------------- 1 | # Day 15: Pattern matching (part 2) 2 | 3 | Last time we started our journey on pattern matching. While I am not going to cover everything in pattern matching I would like to continue from where we left off last week and cover some more patterns you are likely to use. 4 | 5 | Last week we discussed the constant pattern, which is as it sounds. You are matching against constants: 6 | 7 | ```fsharp 8 | let isTrue bln = 9 | match bln with 10 | | true -> "Yes" 11 | | false -> "No" 12 | ``` 13 | 14 | And we had the identifier pattern, where by you state a discriminated union label you wish to match against, our example being: 15 | 16 | ```fsharp 17 | let sendMessageToCustomer contactMethod = 18 | match contactMethod with 19 | | Email email -> printf "Sending an email to: %s" email 20 | | Mobile mobNumber -> printf "Sending text to: %s" mobNumber 21 | | Letter address -> printf "Sending letter to: %s %s, %s" address.Line1 address.Road address.PostCode 22 | ``` 23 | 24 | In the identifier pattern you'll notice the value we unwrap from the label is assigned to a variable. Assigning the variable is of course very useful as we have it unwrapped to the type we need it to be. This allows the above example to get access to the address type properties. 25 | 26 | Another benefit of assigning a variable to what you are matching means that you can apply a _when_ guard: 27 | 28 | ```fsharp 29 | let rand = new System.Random(); 30 | match rand.Next(0, 20) with 31 | | a when a >= 10 -> printf "Higher than 10: %d" a 32 | | a -> printf "Lower than 10: %d" a 33 | ``` 34 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBATghgdgEwgXgnEB3CBlAngZzBAFsA6AJXgQHtiAKASgG4BYAKHeJjAGMALaFVIA5EAA8wdAAwAaCACYpDCBgCWYPuwA+EGCr4g4uiAD5UARikQAtCYgAHKKrhgAZhABEACVUBzA1AQGvAQlgBcEACkCB662sa2Dk4u7h4AMtQYIIHBRuFRMbpAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 35 | 36 | A word of warning about the when guard. It comes at a cost, because the compiler will _not_ look inside the when guard to work out if all eventualities are handled, and as such the following would tell you that you have not handled all eventualities, even though we know it has been. 37 | 38 | ```fsharp 39 | let rand = new System.Random(); 40 | match rand.Next(0, 20) with 41 | | a when a = 10 -> printf "Higher than 10: %d" a 42 | | a when a <= 10 -> printf "Lower than or equal to 10: %d" a 43 | ``` 44 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBATghgdgEwgXgnEB3CBlAngZzBAFsA6AJXgQHtiAKASgG4BYAKHeJjAGMALaFVIA5EAA8wdAAwAaCACYpDCBgCWYPuwA+EGCr4g4ulBACMUiAFoAfBAAOUVXDAAzCACIAEqoDmBqBAa8GZSAFwQAKQI7rraxhgGRnoAPKjmVrYOTq4eADLUGCABQUbUASAAjgCuMMCB1CHhUTEwQA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 45 | 46 | Record pattern - there is now an equivalent of this in C#, it allows you to match against part of the record: 47 | 48 | ```fsharp 49 | type Contact = { 50 | FirstName : string 51 | LastName : string 52 | } 53 | 54 | let whoIsIt person = 55 | match person with 56 | | { FirstName = "John" ; LastName = "Harman" } -> printf "John is the best" 57 | | { LastName = "McEnroe" } -> printf "You cannot be serious!" 58 | | _ -> printf "Not sure who this is: %A" person 59 | 60 | let john = { FirstName = "John" ; LastName = "Harman" } 61 | whoIsIt john 62 | 63 | let mcinroe = { FirstName = "John" ; LastName = "McEnroe" } 64 | whoIsIt mcinroe 65 | 66 | let imposter = { FirstName = "john" ; LastName = "Harman" } 67 | whoIsIt imposter 68 | ``` 69 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAwg9gO2AQwMbCgXigbwLABQUxUAYgJYBOAzsAHLIC20AXFLZeQgOaElQAZZLQbMobDl14EAvoUIAbCBgDuACzgBJapoyQaiLHxKNkwVGqj7qhleWBrjxAD64yVEU2jYARACk4NQQfKABuQWF6LywoHwAJZEpTYKgZKABaAD4rTiQAM1iAoKhyaigHaAAjCFofJyhXHAjPMV8AWVQAUQRKOAgQtKycrmACnwBNOABXKFRkBAQ4DGr2CE5p6gBCOqISVwB9DOywXNHYuiX2KcpodThytVKS6jYAUgBBEOtDeQIlDAAVoEEDEmhQaFFWoVgSFwkIWt5YgkkvMBoQ7tpdFAgUFfv8oIxUFxeoiwR5IYj-DCws0KTEfB1uiS0QQMToMITiX08coSowwHBaGtQe4IaJKTiUnDIuL6cjkiy2VjyPzBcA1oQgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 70 | 71 | NULL pattern - Yes the dreaded `null`. Only really necessary when dealing with BCL libraries, rather than F# libraries. Useful none the less. 72 | 73 | ```fsharp 74 | let isItNull (str : string) = 75 | match str with 76 | | null -> printf "Is a null" 77 | | a -> printf "%s" a 78 | 79 | isItNull null 80 | isItNull "This ain't no null" 81 | ``` 82 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAlgzgkmAcgV2MCAKGYBOEBcE2OUAdgOYCUEAvALABQEzEAtgIZgDGAFkbhADuUMD0YsIAHwilU6ALQA+CAAcSpMADMIAIjgwI7GXJ3iW0o0tXqtugKQwdhxo1gIUaY2lfwkc3QAqPLCGZADkkKQA9l7AOkA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 83 | 84 | One last one for the day, the Tuple pattern. Pattern matching only allows for a single input, however we can get around this with a tuple. Simply supply the values you wish to match with a comma between them, and in the patterns be sure to supply one match per comma. 85 | 86 | ```fsharp 87 | let optionEqualOrGreaterThan a b = 88 | match a, b with 89 | | Some x, Some y when x >= y -> printf "true" 90 | | Some _, None -> printf "false: left some, right none" 91 | | None, Some _ -> printf "false: left none, right some" 92 | | _ -> printf "false" 93 | 94 | optionEqualOrGreaterThan None None 95 | optionEqualOrGreaterThan (Some 1) None 96 | optionEqualOrGreaterThan None (Some 1) 97 | optionEqualOrGreaterThan (Some 1) (Some 1) 98 | ``` 99 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBA9gDmCWUB2BRAjgVwIbAPICcBxfELMEfAFQAsskIsIAjCAXgFgAoCHiAWzIBjagwA0zCAHd4Yal14QAPhADKUPiAgAPcWo0QAnlOoh6WiAD5WhiAFoLEGPnhIwAMwgAiMPgwhP8rzKepoA+uIAcsia9o7Orh6ebjgAziAAXBCgbpAp6iDizgDm1JBI0QHcQRBRSAWq+RChdg5OLu5eycBpmdll0YXwJbn5lQrKzbFtCZ2p-lxcsAjI6Nh4RCRkFDR0NdF7dYtwiKiYOATEpORUtPQAFCEQAIwAlAcgR8unaxeb1zv0WqaB6NV6fE6rc4bK7bW4QEH6V7wx6vIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 100 | 101 | There's more going on in the above, so we'll break it down piece by piece. We are accepting two option values of int, and if they are both Some then we can compare then inside the when guard and print true to the screen. I then over engineer the scenario to show off the ability to check if either side is Some with a None, before finally outputting false. 102 | 103 | oh, I forgot to mention that F# knows that both parameters are the option type, because in the pattern matching we've used the Some and None labels. Once the values are unwraped it can see we're doing a comparison using greater than, which by default it considers to be an integer. -------------------------------------------------------------------------------- /Lessons/day-09-discriminated-unions.md: -------------------------------------------------------------------------------- 1 | # Day 9: Descriminated Unions 2 | 3 | A descriminated Union has no direct comparison in C#. At a stretch you could say it's like an enum, or similar to an abstract class with it's various implementations as the various options. 4 | 5 | Neither are ideal as they have problems of their own. For example, any ```int``` wrapped in the ```Enum``` type is valid, even if you only have 3 options that you have defined. The same could be said of an abstract class, you may create only the implementations you consider valid, however another developer could add another implementation and may not handle this new implementation everywhere it is required. Not to mention how verbose creating an abstract class, and it's inheritors is. 6 | 7 | Discriminated Unions can solve both of the above problems in a simple to use syntax. 8 | The syntax is fairly simple, you state the name of the discriminated union, and then a pipe ```|``` character followed by each item's label. 9 | 10 | ```fsharp 11 | type PreferredContactMethod = 12 | | Email 13 | | Mobile 14 | | Letter 15 | ``` 16 | 17 | The above example is similar to an Enum, except the only possible values for that union are the ones I have defined. That's not all though: 18 | 19 | ```fsharp 20 | type PreferredContactMethod = 21 | | Email of string 22 | | Mobile of string 23 | | Letter of string 24 | ``` 25 | 26 | This time we've stated that each of our labels should hold data ```of``` a string type. One way of thinking about the label is as if it is a wrapper to the data. The label isn't the data itself, it merely acts as a way of discriminating between the various labels available on this union. Once you know the label, then you know the data within it. 27 | 28 | You can place any types you wish inside the label, for example our Address Record Type from yesterday can be used to store the address details for the letter. 29 | 30 | ```fsharp 31 | type PreferredContactMethod = 32 | | Email of string 33 | | Mobile of string 34 | | Letter of Address 35 | ``` 36 | 37 | The flip side of that is that you can use a Discriminated Union inside a record type too: 38 | 39 | ```fsharp 40 | type Customer = { 41 | FirstName : string 42 | LastName : string 43 | ContactMethod : PreferredContactMethod 44 | } 45 | ``` 46 | 47 | Discriminated unions are no different to Record Types with regards to equality, we can compare two different variables in exactly the same way as before: 48 | 49 | ```fsharp 50 | let preferEmail = PreferredContactMethod.Email "john@johnharman.co.uk" 51 | let preferEmailDuplicate = PreferredContactMethod.Email "john@johnharman.co.uk" 52 | 53 | printf "Are these unions equal? %b" (preferEmail = preferEmailDuplicate) 54 | 55 | let customer = { 56 | FirstName = "John" 57 | LastName = "Harman" 58 | ContactMethod = preferEmail 59 | } 60 | let customerDuplicate = { 61 | FirstName = "John" 62 | LastName = "Harman" 63 | ContactMethod = preferEmailDuplicate 64 | } 65 | 66 | printf "Are these customers equal? %b" (customer = customerDuplicate) 67 | ``` 68 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuKFYRSgcwOKgCUB7AQzj1GzNh2IBhMqCFMWFdoWIAFHk3E840BrNGKi6gK4VmIGSPkEAvgQKhIUJcgBmERMjjrjfAMbAAshDAABYamGJEAD5QAKIAtnxkADZQPE7CcgqcUX48AEZJ0KnpulmkgcCuKWnwSKgoNvh20OIGTDyxlVh4elAAYmSITAByfB1mGeGkfMOjWsUWPZ7APv6BIYIMjhAubhAePF6+AcEaVg2JgVBgzq5xCclYWzvuSyvH6wB0d0lQAEQAVjwghQAAKA4FBPiIeIUD7eHgfAwAa1+BAuwCuN0Q30SABEDGBEmRvHwKpgHFiXgdlkc1hovvEfgCgaDwRRIdC+LD4YiUQ1rpRgGlfjBkFBgqhoEYyAd0BAAI4GPiJAD8UAApLlflAABTXba3RkPTEG7FG-GE4mkiAASnOl28rWA7U6OEm-UGwBGYywvwAUizUT0SNMvbNyb8ABJQmFBzivWknQRYfU7HFnfDoqCOtodRAWokksldd0DGY+v4B4Fx4gh8vQX3RzkUGv6alvOnJk1p80EwvWjMEAXGYWi6ASlDQHPOvNyxXKtWa7U66cuxDk1d5gtWio2oA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 69 | 70 | So what would happen if we tried to compare the simple type value of email, a string, with the descriminated union value? It would fail. 71 | 72 | ```fsharp 73 | let email = "john@johnharman.co.uk" 74 | let preferEmail = PreferredContactMethod.Email email 75 | 76 | printf "%b" (email = preferEmail) 77 | ``` 78 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuKFYRSgcwOKgCUB7AQzj1GzNh2IBhMqCFMWFdoWIAFHk3E840BrNGKi6gK4VmIGSPkEAvgQKhIUJcgBmERMjjrjfAMbAAshDAABYamGJEAD5QAKIAtnxkADZQPE7CcgqcUX48AEZJ0KnpulmkgcCuKWnwSKgoNviJgVAQ8UmYUABEAFY8QRQAAr39QXyI8RQAdN48kwYA1p0ETcBQYM6ucQnJWI4QLm4QHjxevgHBGpNb7a3bDeuUwGmdAKS5nVAAFLftWOv7mzaiQAlAQgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 79 | 80 | The reason for that is because everything in F# is explicit. The F# compiler can infer which types you mean, but it will not cross that line into considering two different types, inherited or otherwise, as the same. This may feel like a step backwards compared to C#, but ultimately that is how F# is able to infer the types that you are using. A type can mean one thing, and one thing only. In C# you can state one type and the C# compiler will happily infer the types for you at the appropriate time, for example an concrete implementation of an interface. 81 | 82 | One way to handle the above comparison would be to put them both in the same union type and then compare. The alternative is to use pattern matching but we'll save that for another day. 83 | 84 | Everything I've done up until now always includes the discriminated union name, as well as the label. It turns out F# only needs that discriminated union name if the labels clash with other labels / types. Otherwise it knows the label belongs to the union, and as such you don't need the union's name at all: 85 | 86 | ```fsharp 87 | let preferEmail = Email "john@johnharman.co.uk" 88 | ``` 89 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAggJnAThAziqBeKBvAsAKCiKgBkBLAOwgEYoAuKFYRSgcwOKgCUB7AQzj1GzNh2IBhMqCFMWFdoWIAFHk3E840BrNGKi6gK4VmIGSPkEAvgQKhIUJcgBmERMjjrjfAMbAAshDAABYamGJEAD5QAKIAtnxkADZQPE7CcgqcUX48AEZJ0KnpulmkgcCuKWnwSKgoNvh20OIGTDyxlVh4elAAYmSITAByfB1mGeGkfMOjWsUWPZ7APv6BIYIMjhAubhAePF6+AcEaVg2JgVBgzq5xCclYd0lQAEQAVjxBFAACH19BfEQ8QoADpvDwQQYANYvAgXYBXG6IJ6JAAiBjAiTI3j4FUwMXiz3enx+fwoAKBfFB4MhMIa10owDSLxgyCgwVQ0CMZAO6AgAEcDHxEgB+KAAUlyLygAAprttboSHoiFcilejMdjcRAAJTnS7eVrAdqdHCTfqDYAjMZYF4AKRJsJ6JGmVtm+JeAAlAcCnZwlitjut8fKdiizvh4VBDW0OogNVicXiuuaBjMba8HV8-cQXenoLbvZSKDn9Adlkc1qEsKHFfcE1qKhGCAzjMzWdAOShoDHjXG+YLhWLJdKZb2TYh8eO4w2k7qgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 90 | 91 | It's fair to say that a discriminated union is the OR to the record types AND, again described as such by Scott Wlaschin. Combining the two together can give you a very powerful way of expressing what your model should contain, without the need to explain verbally or via comments what should or shouldn't be set in X scenario. This allows us to avoid nulls and thus reduce possible bugs in our code. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fsharp-a-beginners-guide 2 | 3 | This repository is a series of small lessons that I wrote for my colleagues at work, in an attempt to get them interested in F# with the possibility of using it at work in the coming months / years. My journey to F# was not quick or direct, in fact it's still very much ongoing, but I think these lessons have helped get the team interested, even if we haven't yet started using it on commissioned work yet. 4 | 5 | I've accumulated my knowledge through various sources. Firstly by listening to Dave Mateer ([@dave_mateer](https://twitter.com/dave_mateer)) give a talk at [@dotnetsoutheast](https://twitter.com/dotnetsoutheast), where he walked us through his recent learnings from what he refers to as the "Orange Book", aka [Functional Programming in C Sharp](https://www.manning.com/books/functional-programming-in-c-sharp). He talked through his learnings from the book, which he'd also discussed in a recent [blog post](https://davemateer.com/2020/03/06/Orange-Book-Functional-Programming-in-C-Sharp). I thoroughly enjoyed the talk, but when looking into using one of the referenced libraries, [Language-Ext](https://github.com/louthy/language-ext), I found it all rather daunting. The documentation was definitely not written for someone learning a functional approach to programming, although [Paul Louth](https://twitter.com/paullouth) did provide a very interesting set of [wiki pages](https://github.com/louthy/language-ext/wiki) talking through various subjects. I read the Orange Book from front to back in a fairly short time frame, and found the concepts appealing, although the implementation was often frustrating for someone new to the subject. I found it hard to realise where the limitations of the approach were, and would often battle generics and extension methods to try and implement what I wanted. 6 | 7 | A few months later I saw [Mark Seemann](https://twitter.com/ploeh) promote his Clean Coders series, [Humane Code](https://cleancoders.com/series/humane-code-real), on twitter. I decided to dive in and watch all of the videos in one evening. My main memory from the series was the concept that we read a lot more code than we write, and that optimising our reading of code would mean that the code we write can more expressive and make us more efficient. In the series Mark discusses various concepts, which I won't try to paraphrase or summarise because I would likely fail, but a lot of what was discussed was based on functional principles. Which to be honest felt hard to take on in the first watch through. After watching the series I then progressed with applying functional practices in C#, this time trying hard to use Language-Ext some more. Although it made more sense this time around. 8 | 9 | This journey was both enjoyable and frustrating in equal measure. Everytime I solved a problem I found another problem that I didn't know how to solve, mostly because I wasn't sure how to compose the various generic typed extension methods to do what I wanted. 10 | 11 | I watched more YouTube videos from Mark Seemann, and others on the subject of functional programming too. At the start of 2020 I realised I wanted to start learning functional programming properly, and started preparing myself for the fact I was about to dive into a world that was harder than anything I'd attempted before. I'd been hearing about Monads, Applicatives, Lambda Calculus and function signatures for a while now, and while I understood some of the concepts, I couldn't explain them. 12 | 13 | Luckily I came across [Isaac Abraham](https://twitter.com/isaac_abraham)'s talk on YouTube called [Go Pro on .NET with F#](https://www.youtube.com/watch?v=UXeFR5RQnjs). It was a breath of fresh air, I found it easy to understand and there was no complicated terminology. With a renewed hope of being able to learn the language fairly quickly, I turned to the often suggested [fsharpforfunandprofit.com](https://fsharpforfunandprofit.com), written by Scott Wlaschin, but I found this a hard resource to learn from. In hindsight I can recommend it as a good reference for syntax and concepts if you need a refresher. After that I found Isaac's book, [Get Programming with F#](https://www.manning.com/books/get-programming-with-f-sharp). While the book is a few years old most aspects of the book are still relevant. The only area where it is now dated is with tooling, of which [Ionide](https://ionide.io/) is a great editor plugin for VS Code, while Rider cannot be rivalled for solutions that include C# and F# code. With my desire to know more I then dived into [Domain Modeling Made Functional](https://pragprog.com/book/swdddf/domain-modeling-made-functional) by Scott Wlaschin which cemented my intent to start writing F# at work. 14 | 15 | It was at this point that I discussed F# to my colleagues, who were interested but also skeptical. I knew they wouldn't find the time to sit down and read either book, which meant that writing F# at work was a lot less likely. I could try and get them to watch Isaac's Go Pro on .NET with F# video, but that is unlikely to happen at an hour long in run time. It was at this point that I thought I could explain the core features of F# that make it so simple to use, but powerful at the same time. Not only that, but I'm still fairly new to F# and would likely relate to how the rest of my team think, rather than being an expert in F# and forgetting about some of the basic concepts that you forget about after some time. These explanations may not be 100% correct, but they would get my colleagues 80% of the way there. 16 | 17 | One aspect that I've not yet covered in my lessons is the concept of using F# to serve HTML from the back end with the likes of Giraffe and Saturn. Not to mention writing front end code with Fable using Elmish and MVU. Each of these aspects has been encapsulated in the SAFE stack. A few great resources that I have found on this subject are: 18 | 19 | * [SAFE stack](https://safe-stack.github.io/) 20 | * [The Elmish Book](https://zaid-ajaj.github.io/the-elmish-book/#/) written by [Zaid Ajaj](https://twitter.com/zaid_ajaj) 21 | * [Reinventing MVC pattern for web programming with F#](https://www.youtube.com/watch?v=deHj2lG5qOY) by [Krzysztof Cieślak](https://twitter.com/k_cieslak) 22 | 23 | I'd like to give a big thanks to all of those I've mentioned above, without them I wouldn't have learned all that I have about F# so far. My hope with this repository is that it will help others learn F# in small increments, without needing to commit too much time. I think all the lessons can be read within 20 minutes. I'd recommend re-writing where necessary, based on the experience your team has and the understanding they have with the various concepts that we discuss. It's also important to encourage the team to play with the code as much as possible, this helps because the new syntax to C# and curly brace developers can feel alien to read at first, the more you write in F# the quicker it becomes easier to read and more natural to write. 24 | 25 | Learning F# has been useful in many ways, not least because it has helped me to embrace what C# is and isn't. There were times during this functional journey where I was trying to push C# too far, I wasted time trying make something work where it really isn't appropriate, even if it is technically possible. I think my C# has improved in this last year, and that in itself makes it worth learning new concepts and approaches. 26 | -------------------------------------------------------------------------------- /Lessons/day-07-compiler-'magic'-and-even-more-functions!!.md: -------------------------------------------------------------------------------- 1 | # Day 7: F# Compiler "magic" and Even More Functions!! 2 | 3 | Up until now we've managed to avoid specifying any types in our F# code. I imagine that makes you feel uneasy, after all you are used to seeing what the function has as inputs and outputs as a glance. 4 | That being said, people also felt uneasy when C# first introduced the `var` keyword: 5 | 6 | ```csharp 7 | // Explicit variable type, the go to 8 | int age = 20; 9 | // To inferred type, what sort of horrible-ness is this!! 10 | var age = 20; 11 | ``` 12 | 13 | The F# compiler is like the above, but on steroids*. While that is an overly simplistic comparison, it feels pretty apt. 14 | 15 | \* I have to chuckle when saying that, because the "[Get Programming with F#](https://www.manning.com/books/get-programming-with-f-sharp)" book said that many F# features were like X on steroids. *A lot of examples from this lesson are taken from Isaac's book, I'd recommend reading it to get a more verbose explanation of this topic.* 16 | 17 | Your code will often drop hints as to what you are expecting based on what your functions are doing. For example: 18 | 19 | ```fsharp 20 | let getLength name = name |> String.length 21 | ``` 22 | 23 | The fact that `String.length` expects a string means that name should be a string. 24 | The above F# book gives a very good example of code that doesn't give the compiler enough information to infer the type of name: 25 | 26 | ```fsharp 27 | let getLength name = sprintf "Name is %d letters." name.Length 28 | ``` 29 | 30 | Here F# only knows that the name variable has a Length property, but that's not enough to isolate the type in use. 31 | 32 | Sometimes we do need define your parameter types up front, this common for classes that come out of the BCL, but it can also be true for your own types if they happen to overlap heavily. 33 | 34 | To resolve the compiler error above you can specify the type as such, by wrapping them in brackets and stating the type. 35 | 36 | ```fsharp 37 | let getLength (name : string) = sprintf "Name is %d letters." name.Length 38 | ``` 39 | 40 | Previously I've mentioned that F# can infer that any parameter either side of the `+` operator should be an int, but we all know that you can `+` many types including floats `/` decimals and even custom types that implement the operator. In this case though, F# has decided that the default type for the `+` operator is an int. 41 | 42 | If you actually want to define a function for a `float`, you would do it as such: 43 | 44 | ```fsharp 45 | let sumFloat (a : float) b = a + b // float -> float -> float 46 | 47 | printf "%f" (sumFloat 1.2 1.2) 48 | ``` 49 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAzgrgtgMWAewIaQBSogLggMxXQEoIAjCAXgmwGpyBYAKGYAcAnASwDsx8IARAFJ8AiBliIikAIwA6AEwR5C4kA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 50 | 51 | Notice only the first parameter required wrapping, it could then infer that the rest would also need to be a `float`. 52 | 53 | Inference has it's obvious benefits, but there are even more powerful benefits you won't wouldn't have even thought about until the compiler does it for you. 54 | 55 | ```fsharp 56 | let concat item1 item2 = [ item1 ; item2 ] // 'a -> 'a -> List<'a> 57 | ``` 58 | 59 | Above I've declared a function that expects 2 parameters and returns them in a list*. Looking at the signature you'll see it's decided that the inputs and the output list are a generic type, very cool! 60 | 61 | \* The angle bracket syntax in F# is for a list not an array, it is NOT the same list type that you are used to in C#, because it's immutable as well as other properties. We'll discuss collections in more detail another day. 62 | 63 | The above may seem obvious, but it does this with non trivial examples too. :siren: This code is pointless, but it shows generics being applied again :siren: 64 | 65 | ```fsharp 66 | let arbritraryFunction map item1 item2 = map item1 item2 // ('a -> 'b -> 'c) -> 'a -> 'b -> 'c 67 | ``` 68 | 69 | _You may find this example complicated while you are still learning how to read functional signatures, my apologies if it's too hard to follow but I am deliberately trying to make this example a little harder to demonstrate the feature._ 70 | 71 | F# has looked through the function body and worked out that the map parameter is a function, and that it is expecting two inputs, which it defines as types `'a` and `'b`. It doesn't know what map would return, but it doesn't need to because it can apply an additional generic parameter to it as `'c`. This then means that `item1` must be of type `'a`, and `item2` must be of type `'b`. 🤯 72 | 73 | When using that implementation `'a`, `'b` and `'c` could all be of the same time of course, but they could be completely different types. It all depends on how you use it. 74 | 75 | Perhaps you would find the Tupled version of the same function easier to read (notice the slight difference in the function signature, hint: `*`) 76 | 77 | ```fsharp 78 | let arbritraryFunction func item1 item2 = func(item1, item2) // ('a * 'b -> 'c) -> 'a -> 'b -> 'c 79 | ``` 80 | 81 | When your code isn't compiling as you'd expected, it can be daunting at first. In functional languages they often say to "follow the types". What does that mean? Essentially take your time and think through the functional signatures, check what you are expecting and what the compiler says it is expecting. I often find similar issues in C# when working with inferred generics especially with abstracts and implementations. 82 | 83 | Here's another example ripped straight out the book, you'll need to look at this next set of code in the try.fsharp.org tool so you can see the compiler error 84 | 85 | ```fsharp 86 | let sayHello(someValue) = 87 | let innerFunction(number) = 88 | if number > "hello" then "Isaac" 89 | elif number > 20 then "Fred" 90 | else "Sara" 91 | let resultOfInner = 92 | if someValue < 10.0 then innerFunction(5) 93 | else innerFunction(15) 94 | "Hello " + resultOfInner 95 | let result = sayHello(10.5) 96 | ``` 97 | [Try the Code](https://try.fsharp.org/#?code=DYUwLgBAzghgngCRMYB7AFFVBbEA1GYAVxAEoIBeAWACgJ6JRIBLAO1ZACcAxI1gYzDNUrdKyLYARl3LU6DBcwBmEcVK4QAfBABEAC2RodEMAda6AkrBj8dtBQuTLVE6Zy0QATAAYTZ3dycIAAmdvIOEMhQILoAyjCcMGH2DEwQQVBEwGAA8koW7BpyEfTOWLgExDEAPBAAjN4AdL6mIOZsHDx8gsKiAKykKRFRMR1cvAJCIuh1A7RDukgoqLoQANTpIJnZeQWd8zRpGVmQFNDwS2gzTQNAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 98 | 99 | You'll notice the compiler has 3 different warnings, but the problem is actually higher up in the function. If you hover over innerFunction (on try.fsharp.org) you'll see that it's expecting a string, not an int. That's because we're doing a comparison with a string in the first line, so it then assumes everything else is wrong. 100 | 101 | If something like this is confusing you, and this applies to C# generic types too, a helpful thing to do is to explicitly state the types you are expecting. In this case if you updated the innerFunction definition to: 102 | ```fsharp 103 | let innerFunction(number : int) 104 | ``` 105 | You should then see that the actual issue being highlighted correctly. Correcting that if statement so that it correctly compares to a number (for example, `10`) then means everything can compile, and you can happily remove the type from the function definition should you wish. -------------------------------------------------------------------------------- /Lessons/day-10-modules.md: -------------------------------------------------------------------------------- 1 | # Day 10: Modules 2 | 3 | The last two days we covered the data aspect of F#, of which I have not covered exhaustively. You can still fallback on all the usual BCL features if you wish, such as interfaces, classes, abstract classes etc. In the middle of your F# code you'll gnerally stick to F# features, but when it comes to interop with other libraries, often those written in C#, you'll have to fallback on those features. I'd recommend getting your head into a book to get more indepth information on this subject on the whole, but I think this serves as a helpful first pass. 4 | 5 | So what about the functions, or the behaviour of the application, where do we place these to keep them organised? I always imagined that F# being a functional language meant that you can just define a function wherever you choose, akin to JavaScript / TypeScript, but that's not the case. To write a function in F# you will _generally_ write them inside a Module. "Hold on John, we've been writing functions without any modules in sight for two weeks!", yes you are right. With the try.fsharp.org website we've been writing what are known as script files. 6 | 7 | F# has three types of files, but the ones of importance for you at the moment are Source files (`.fs`) and Script files (`.fsx`). The last type is a Signature, but I don't know what that does yet. 8 | 9 | Script files are essentially a scratch pad for you to run F# code ad-hoc, much like we have been the last two weeks. Script files are not compiled down into anything re-usable or executable, they are purely for running code ad-hoc. They are really useful for having a little play around with some code, much like you might test some C# in LinqPad. Any function written inside a script file does not need to be associated to a module. You can reference assemblies and execute code without the need to compile too, but let's leave that for now. In fact in F# 5.0 coming later this year, you'll be able to reference Nuget packages directly from your `.fsx` file, which brings some fantastic opportunities for scripting. 10 | 11 | Source files are what you include in your project, they compile down into a dll just like your usual C# projects. However, to do that your function must belong to something, and in the case of F# they belong to modules. What are modules? You can think of them as static classes with static methods, which are public by default, but can be private too. 12 | 13 | Personally I find there is often some ambiguity in the current ecosystem of C# as to where we should put functions. For example, these days we create a lot of POCOs and DTOs in C#, but where do we place the logic relating to these? Sometimes it's on the model, sometimes it's an extension method and sometimes it's in a handler. 14 | 15 | In F# there are three common ways to layout your types and modules, but I think my preferred approach so far has been to define the Types up front, and then lower down in the same file define the Module for these types. 16 | 17 | Below is an example of defining some types, and then some modules. We'll then use the functions we've defined to filter a list of customers based on their contact preference. I would recommend reading the below example at try.fsharp.org because it gives you a tiny bit of syntax highlighting, not much but it's something to help break down this large block of text. 18 | 19 | ```fsharp 20 | // Define our types up front, and because declaring these types is so light in syntax 21 | // you would declare many in one file, normally related to one another of course 22 | type Address = { 23 | Line1 : string 24 | City : string 25 | PostCode : string 26 | Country : string 27 | } 28 | 29 | type PreferredContactMethod = 30 | | Email of string 31 | | Mobile of string 32 | | Letter of Address 33 | 34 | type Customer = { 35 | FirstName : string 36 | LastName : string 37 | ContactMethod : PreferredContactMethod 38 | } 39 | 40 | // Now we'll define some modules on them, I've split them up into modules based on the Types name 41 | module PreferredContactMethod = 42 | let isEmail contactMethod = // PreferredContactMethod -> bool 43 | // This is pattern matching, well see look at this soon 44 | match contactMethod with 45 | | Email email -> true 46 | | _ -> false 47 | 48 | module Customer = 49 | let findAllEmailContacts contacts = // List -> List 50 | let customerPrefersEmail customer = // Customer -> bool 51 | customer.ContactMethod |> PreferredContactMethod.isEmail 52 | contacts |> List.filter customerPrefersEmail 53 | 54 | // Let's make some customers 55 | let customers = [ 56 | { FirstName = "John" ; LastName = "Harman" ; ContactMethod = Email "john@johnharman.co.uk" } 57 | { FirstName = "Bill" ; LastName = "Harman" ; ContactMethod = Mobile "0800-dial-a-dev" } 58 | ] 59 | 60 | // Print out the results to see that John "Never checks his emails" Harman is the only value output 61 | printf "%A" (Customer.findAllEmailContacts customers) 62 | ``` 63 | [Try the code](https://try.fsharp.org/#?code=PTAEBEFMDMEsDtKgPYFcBOoAuBPADpAM6ip6jTrLxYA0oAhvACagBGkAxvaoUk5wBt66BAHNsACyJJcBYrGKFkoAbFESsoBKEI5q9AB4BYAFAhQONKADuaAS34ch6JAFtGOLfBSJysAZB08Mjo7gICni5CWJAsWMpUSIzIWFKYyNCgHGjovKaySACCTEwuhMQAvKAA3qag9aAAMgiQAIygAFw6WCLwonUNAMKwuJ3dvf0mDaAACsiEWIPI-GMLEwP1S6jU6J5da2KmAL6m+fhIMy7QkOguTEv6HFgAspCpy6AVG6AAPqAAou5-ChMgc+t8-s9kKx-EgMuNDlMGn9Gm8YulMsVSkRCKcTAVQIMePFXDdPjVvgAxWC5LAAOXopNWPUR00a9AWDKZ+xZ4KRmyoWHoT1e7xYXUuMBudweQpFbwky2OePMdOQ1hskAA5OFQPw4L4lEzXMtUAFiFRJJBXHQAJJagBuSEIeFUmlS1pIZAQ8VAJqYZqIbA5sR8VtAABVzsR4IzIKZ-YHZldpbFZcKXgqPl9+So3lpCID6MDso9M2LyeZJddbmnBRnRYqWABaAB8bGQyAE32m5gjEgUBdAeHoWHR3ncWA4A76dGskF1vCQAk7AGsGO6B4pO-Aew1J9OsvX5RXrCMJHv6n8i8DrcWBKA29h0Kh47npn8APqP9vQegCPITATU0AkJYlkFJTAc2mAJNANJhCnCG8BHTJ5iFLOUsEqUBzGaBYAB4iQWCCbnbJ88KwQjwMg1tLzzTQOGom5qxuQsgQfRjiMgyswCIkkySfVhO27d9pgaTj+PQAA6VDyybX52xY2t7mPOTlikhRkNAOiMIzYgfnbCipLgAR0SyJj0CUtj7xVMBUSwLViHcVdnRI8yuNY0xYPcyTsIAbW+apQGpWkuSQKoACIAClkAkeAItAABuJoOXpONyQigAJYR3HipLCVUxtswBdjQAigArWL4AAAUquKJByxgpOyKTUFXBKTlzIKQs5dLIoAIX8AQEuS9leqZSLstCRgRoKssipYKooRhUCIoABgADjWtbmyYWB-2behdsgB0OtMABdWzkx9FBUE3JAyjNLDsGUJdJFHUAYrisq6ROslp04VdiC3UA738QgEqm3Khw9HwIlAB1-1fW6sDwO7TDwXosEyCKAFJCgSgAKPiSOk+DEIEZDZPQizCAASiAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 64 | 65 | There is not as much to unpack in this snippet as it may look. The types you've seen before, they aren't new. In the modules we have created a few useful functions which I've added signatures to via comments for you to see, hopefully these signatures will help you unpack what they are doing. 66 | 67 | Remember the last entry in the list is the output, and everything before that is the inputs. They are all separated by ```->```. 68 | 69 | First the PreferredContactMethod module gives us a function to simply help decide whether the preferred contact method is email or not. It's using pattern matching, we'll come back to that beautiful feature another day. You can probably understand what is happening if you know the new C# pattern matching expressions. 70 | 71 | Customer.findAllEmailContacts is a little bit more involved. First we define a new function to act as the predicate for the filter. The last line is the return value of the function, an equivalent in C# would be ```contacts.Where(customerPrefersEmail)``` 72 | 73 | Nothing too scary once you break it down. It is tempting to write the equivalent C# code in full, and give you an idea of the amount of code involved between the two, but I really can't be bothered to type out that much code for the sake of a size comparison. 74 | 75 | You may find reading this amount of code harder to do outside of an IDE. When you are in a proper IDE it does make reading the syntax easier of course. In fact I would highly recommend it if you have the time. -------------------------------------------------------------------------------- /Lessons/day-17-lists-part-2.md: -------------------------------------------------------------------------------- 1 | # Day 17: Lists (part 2) 2 | 3 | Yesterday we looked at how to prepend to a list, and why we prepend rather than append. Let's work through some more examples of common behaviour with lists. 4 | 5 | Concatenating lists is simple enough, we can use the `@` operator or the `List.concat` function. 6 | 7 | ```fsharp 8 | let names = ["john";"peter";"paul"] 9 | let moreNames = ["don";"phillip"] 10 | let combinedLists = names @ moreNames 11 | let combinedAlt = List.concat [ names ; moreNames ] 12 | printf "%A" combinedLists 13 | printf "%A" combinedAlt 14 | ``` 15 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAdghgtiAzhAvBA2gIgFYHsAWUmA3JgA7ggBOJ5MArsJgLoCwAUKJHLlSAHLwkqDJgAmuIqTL4AlsGCyyLDlwgBjXHABGsqCDEAZWYjDI0sBMgACEHn0FXV4DVt36xAQWCQ0x0wB0mlDqMJDo0ELIxHa8AlEQzBAcZFR6YABmEJgApJ6Yrjp6Bv5mKWlQmdl5BZpFHt5gQA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 16 | 17 | We've created, prepended and combined. We've sliced and diced our lists into smaller lists, but we've not discussed how to get an item out of the list. 18 | 19 | The F# equivalent of First is called head: 20 | 21 | ```fsharp 22 | let firstItem = List.head ["peter";"paul"] 23 | printf "%s" firstItem 24 | ``` 25 | 26 | Just like C#, if you try to get `First()` out of an empty list, it will throw an exception. 27 | 28 | If you are unsure as to whether there is an item in the list you can use `tryHead`, which gives you a `Some` / `None`: 29 | 30 | ```fsharp 31 | let head = List.tryHead ["peter";"paul"] 32 | let headNone = list.Empty |> List.tryHead 33 | printf "%A" head 34 | printf "%b" headNone.IsNone 35 | ``` 36 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAFiCGAmEC8EAyBLAzmAdGATgJ4ASciA2gEQBWA9lAHaUDclA7usMJQLoCwAKFCQYCAHK0GIZBGBYwAHmz50DAOYA+HAFEAtgAcwhCAB8NaeXiKkEg-SoZgAZhEoBSAIKVoZOw+eubgBG3qLwElI4AJKYESBAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 37 | 38 | The thing about having an `Some` or `None` is that it is likely you're going to need to do something with it. If that is the case you could use an if statement, but more likely you would use pattern matching. 39 | 40 | If we are doing the above, let's skip to the chase and go straight to pattern matching with the List pattern: 41 | 42 | ```fsharp 43 | let takeRegister who = 44 | match who with 45 | | [] -> printf "Where is everyone?" // 1 46 | | [ "john" ] -> printf "john, thank goodness you are here" // 2 47 | | [ a ] -> printf "%s, where is everyone?" a // 3 48 | | [ _ ; "john" ] -> printf "I don't care who is first in the list, john is all that matters" // 4 49 | | [ a ; b ] -> printf "Thanks for turning up %s, %s" a b // 5 50 | | _ -> printf "Register taken: present %A" who // 6 51 | 52 | takeRegister [] 53 | takeRegister ["john"] 54 | takeRegister ["peter"] 55 | takeRegister ["peter" ; "john"] 56 | takeRegister ["peter" ; "paul"] 57 | takeRegister ["peter" ; "paul" ; "don" ; "phillip" ; "john"] 58 | ``` 59 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBGCGDWICUQHMCWBnMIBOEDuAFgPYQC8AsAFAQ0QC20YAxgfsfqmAVbRAD4QA2gF0IAWgB8EAA7ZUAOzAAzCACIA6gRwgIGCCABuOAJ5F5IAPyqIAehsQAjD1oDBagFZEC866Mky5RRVVT28AGigCaHlYCGQiIgATc3R0CFMAVwhobB0tXOs7CAAmZxpXbIg-KVkFZTUAUnQIwm1dNMMTM0traFt7AGYy-iEIAH0IAG4PLx8q8RrA+tUASQhEswBySCYcnUISPSVUbExdeUidYAwwCNCLvWhgYEjGekYsU8L7ABZhir60wARvN-LUgmoACpRGJpJREXBgDLYeQKZAQDLSCBNCJNXoQEFFACs-3GCwCdWCSDQmBwUDgIHkAC4AiB0IzIA0AILWA79CAANioVBg8GpNzpIhFDPFtNwghCs1UwmlYpQEvlqmk4BwytViHVcqEWp12Gs00V3j1lFFBppn2N2s+5rU0mgGWA1ttsodCqduqmrvdnsDqg2cwt0gIqGeqGkLstPmEQA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 60 | 61 | I tend to think of the list pattern as replicating the list structure that you wish to match. I'll explain what each pattern represents: 62 | 63 | 1. An empty list 64 | 1. List of one item combined with constant pattern, we specify what that single item should match "john". 65 | 1. List of one item, where the value is placed into the ```a``` variable 66 | 1. List of two items, we discard the first value because we don't care what is in there, but the second item must match "john" 67 | 1. List of two items, where the first value is placed into ```a```, and the second into ```b``` 68 | 1. The classic discard option, where by we are accepting anything not yet handled 69 | 70 | As you can imagine the number of combinations is endless. 71 | 72 | There is another common practice with lists, which is where you match on the head and the tail. We've already seen the use of head above, this is the first item in the list. So what is tail? Well if you think back to our Cons, it's a tuple of `T * List`, therefor the tail is the remaining list. That tail may be empty, but it could be another Cons, which will once again have a `T * List`. Using the head and the tail, you can recurse through a list item by item. 73 | 74 | Before we jump into the below, I should quickly explain that in F#, if you would like to recursively call a function you must explicitly state this to be the case. To do this you add the ```rec``` keyword after ```let``` but before the function name. I'll tag the order that our code executes, and explain each point in turn. 75 | 76 | ```fsharp 77 | let takeRegister students = 78 | let rec callRegister students = 79 | match students with 80 | | [] -> printf "End of register" // 8 81 | | head :: tail -> // 5 82 | printf "%s? Here" head // 6 83 | callRegister tail // 7 84 | 85 | match students with 86 | | [] -> printf "No-one present" // 1 87 | | _ -> // 2 88 | printf "Start register" // 3 89 | callRegister students // 4 90 | 91 | takeRegister [] 92 | takeRegister ["peter" ; "paul"] 93 | ``` 94 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBGCGDWICUQHMCWBnMIBOFMFcATEAOzHQgF4BYAKAgYlEmxAGMI3phgk1MceMEVLkqdRpIgBbaGDYALISLIUA7qjAKJUhgB8IAbQC6EALQA+CAAdsqMgDMIAIgCiJQhAD2T1vyzYzhAA9MEQABw6ugYKINCeAFwJUNCowOZWoRAArFG6jLb2YE7OAKToAPwQABI4IEGx8SFhAGx5+ZzcvCgYASlpzRAA7HR5svJKBMSqEBpaeQYmGTZ2ji4Acl5mXiQgKyDookFZAIwLEAD6y1kATO0Fq8UuAMow2Cw9AoGDAMwQ9wwuDw+L1BFNRBQsgAWUa0GDwEFfIzGOiFNbOMyYrGY5x0eGIT59QzOazgHBBADcLms0HwwGcxiAA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 95 | 96 | I've updated our takeRegister function to now print out the register as it is taken. 97 | 98 | 1. There are no users in the register, this is the end of execution 99 | 1. At least one student available 100 | 1. Print that we are starting registration 101 | 1. Calling the register with our list of students, this starts the recursion 102 | 1. Pattern match on the head & tail 103 | 1. Print the studen's name 104 | 1. Call the recursion again with the rest of the list, the tail 105 | 1. Eventually the list will be empty and end up here, at which point we print the end of the register 106 | 107 | There we have it, the basic concepts of lists covered. Everything else is available on the List module, if you take a look on try.fsharp.org you'll see quite a list! 108 | 109 | As I've mentioned before lists are the go to in fsharp, but that doesn't mean you can't use the same concepts for an array when pattern matching. The difference is the syntax of an array, which looks like so: 110 | 111 | ```fsharp 112 | let students = [| "peter" ; "paul" ; "don" ; "phillip"; "john" |] 113 | printf "%A" students 114 | ``` 115 | 116 | F# also has seq, which is an equivalent to C#'s `IEnumerable`. I won't go into this now but it's worth knowing that it exists and it's purpose. As long as you know where using `IEnumerable` in C# makes sense, you'll probably get the gist of seq in F# too. 117 | 118 | Each type of collection has it's own set of methods, you can access these the same as you would access any function for a type, by looking on the module itself. 119 | 120 | Learning how lists work is pretty important in realising how to interact with them and now we know the implementation we can understand why we interact with it in the way that we do, as well as what approaches are available to us. -------------------------------------------------------------------------------- /Lessons/day-12-single-case-unions-as-marker-types.md: -------------------------------------------------------------------------------- 1 | # Day 12: Single Case Unions (SU) as Marker Types 2 | 3 | Single case unions can help us to ensure the integrity of our data whenever we need it. Emails are an easy example of this because an email is represented as a string, but not every string is an email address. Let's say I need to write a function that will email a customer, one of the parameters I would accept is the 'To' email address. 4 | 5 | In my email function I'll create a new instance of `System.Net.Mail.MailMessage`, which requires `MailAddress` as such: 6 | 7 | ```MailAddress from = new MailAddress("ben@contoso.com", "Ben Miller");``` 8 | 9 | `MailAddress` isn't particularly honest with us because if we gave it a badly formed email address, it will throw an exception at runtime. It would be nice if it gave us a heads up, without needing to delve into the documentation. If we knew at compile time that the possibility of an invalid email address is on the table, and that we should handle that fact. 10 | 11 | I know what you're thinking, all email addresses _should_ be validated on the way into the system, so it shouldn't be a problem. In software lots of things _should_ happen, but things don't always work out the way we expect and we don't realise that until someone hits that path of the logic at a later date. 12 | 13 | Yesterday we discussed how we can wrap primitive types with SUs to help avoid bugs at compile time. It turns out that an SU can help us here too. We can create a function that can act as the gateway to creating a valid email address. 14 | 15 | This function can do this in a variety of ways, the first and simplest would be to return a None if the email address is invalid. That doesn't give us a particularly great user experience though, it would be better if we could provide an error message. To do this I'll use `Result`, it is an F# type which allows you to be more explicit than Some or None by providing a different result type on the Good path compared to the Bad path. 16 | 17 | ```fsharp 18 | type EmailAddress = private EmailAddress of string 19 | 20 | module EmailAddress = 21 | open System.Text.RegularExpressions 22 | 23 | let value (EmailAddress c) = c 24 | 25 | // string -> Result 26 | let create str = 27 | // Please do not use this, it's for display purposes only 28 | let emailRegex = new Regex(@"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}\b", RegexOptions.IgnoreCase); 29 | let m = emailRegex.Match(str); 30 | if not m.Success then 31 | // If implementing fully, we could provide any number of failure messages 32 | Error (sprintf "\"%s\" is not a valid Email Address" str) 33 | else 34 | Ok (EmailAddress str) 35 | 36 | // We know, 100%, that this function receives a valid UN number, no extra checks required 37 | let emailCustomer email = 38 | printfn "Emailed customer: \"%s\"" (EmailAddress.value email) 39 | 40 | // We use an outer function to validate the input, and decide the error path 41 | let contactForm emailInput = 42 | match EmailAddress.create emailInput with 43 | | Ok email -> emailCustomer email 44 | | Error err -> printf "%s" err 45 | 46 | contactForm "john@johnharman" 47 | contactForm "john@johnharman.co.uk" 48 | ``` 49 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAogtgQwJYBsCCATDAnCBnPKAXijGyQDcFhp5l0tcCoB7AMyj2HIDsBzALAAoYXBYYArilqJUmHPkJFhUVa0g8oAZRBcIcAHQAVCAA9gBgEoQ+UhNhimyipCx55hKtdOBQqKCWgACjo5RkUoAGMASmIozyE1KAB6ZM5uJH4oAFoAPihrPClgAB5QhgUCABp03j5cr1UfKNxqaC5sYkaklLSABWkEPGgMFigeFl8JYahgAAskPBqkYAByQjYWToxFsBQEEFIJbDAWYcI3FBBupOb9ems+MzieCAB3ApszIIABACIADoAIwA2mhsgAtAAM2QAnAYAPoAUgA1NkALoon5gyEw+EYlEAgw4iHogDeACYqgA2AC+wL+NUeZgA8mBgK53AYAJJ8Ca4ADCQwg0QA3DdvBBfHA4vdUMzTAYALLUSJzIIdMUS1RIDgTaUGLQSSKRCLzCA8bU9VJQbkcJBwPb6C0crJsKRXGpvaCRFhSDCkbAsChIDDQBA8Q48CRwIEQTrsKBsejHaBwRQIJ4eRI9XMwbBBzoasiZYAcQF-JF4AF-KCLcaTKAIPwIFCh2CyFBQeRMPC1zVWiAoYZWpIsgDWUBCnZ7EQHOdzSRUNoA6tBxxM3jUAIxQqFImrzaizBYbCQ8SIctxQXCmyj4JsttsBgCqADlxjG49gahMoGZuGbNUIEicdCFwABHCQkFwDBhDuTsBWmYAWHTTo5S7ZQF0DUs2E0P5yggANImQ1D4wALigGsqxrWtp3oWcCAMfxAn-TtogSVdoGmcNND9GhOndC8r00FCn1DNoT2gTIwAkYAagjAMw0iUNoHNf8Cy2UhqDmeCpSiNxgAQS8ADEthlDDuR4WTfCwpJEGANUOwY8ImMiVoaDY+grJsqA3hWXTsIAHygCcvNQHJ8gwpCuDI9DO26EL80LDTOjyHCeDLKBKz7VKEl9TLjOAMzsBlP4ACsWDmHgfkq6q5nsRAeD+YQCqM0zzOyuqau6hrSojAxfQMCRxxaoQgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 50 | 51 | I know this is not the best example in the world but hopefully you can consider the above concept and apply it to a project you are working on where a string is not simply a string. It's great to know that if you use an SU as a marker type, with the appropriate validation being done at the creation of the type, you can be sure it is safe to work with and hopefully it will be more obvious to anyone working on the codebase what that type is, more than simply being a string. 52 | 53 | These SUs don't have to be strings, they could be any type. Imagine a `Customer` type, and the concept of a `PriorityCustomer`. Perhaps only certain features of your producte are available to `PriorityCustomers`. If you write the necessary business logic into the creation of that SU, then you can be sure that any developer trying to use your function knows that the customer must be a `PriorityCustomer`, and the decision on whether that customer is a priority customer or not is declared in one place. 54 | 55 | Here are a few more examples of encoding business logic into our types. 56 | 57 | A list that must have at least one item: 58 | 59 | ```fsharp 60 | type AtLeastOne<'a> = private AtLeastOne of 'a list 61 | module AtLeastOne = 62 | let create inputList = 63 | match inputList with 64 | | [] -> Error "One item must be provided" 65 | | _ -> Ok (AtLeastOne inputList) 66 | printf "%A" (AtLeastOne.create []) 67 | printf "%A" (AtLeastOne.create ["John"]) 68 | printf "%A" (AtLeastOne.create [1]) 69 | ``` 70 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAgsAyECGBnYB5AdhAPAciQD4oBeKMAJwEsA3JYaORVDbKAewDMoCoAbKmgCwAKFEBbdgBMArn0YJkaLNBKioG-hGBQAxhWQMoVTGBkJBOtSM22o4+roAWx0+fiWoAdyrAn6uw0AHygAbQBdKABaYgBRCgp2CigAIhVjBnF7GTQoACNoSnYaKikIKRSAwJCAfWjidABrKAAKJiVWaBMzCzQASlFRShNgbhSAUhgU1vaWFQA6fUNoCIGRYcxR1MnptsU57EWDehWUgCl2J0wU8LWNrYmpmf3lQ6WTsIBGW9EgA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 71 | 72 | A string that cannot be longer than 10 characters in length: 73 | 74 | ```fsharp 75 | type String10 = private String10 of string 76 | module String10 = 77 | let create (str : string) = 78 | match str with 79 | | str when str.Length > 10 -> Error "String must not be longer than 10 characters" 80 | | _ -> Ok (String10 str) 81 | printf "%A" (String10.create "1234567890") 82 | printf "%A" (String10.create "1234567890!") 83 | ``` 84 | [Try the code](https://try.fsharp.org/#?code=C4TwDgpgBAysBOBLAdgcwIwAYoF5YJQ2wHsAzKAZwLQFgAoegW2IBMBXAG2jiTS13pQhULsCgBjeBACGwaAAoq8KAC5K1VAEoBdYXqiNZ4gBbrlAd0TBjg-UIA+ZqOeMRkZgHQAZN6mtQAPih+AFoggFF4eGJlACIeQgM2KihkYjEAI2gOYjQIZWtpd34TaXhpcTl4CljbO0cAfSgwqAB5AGsoeQS+bCVNenowXmByWIBSAEFYrp6iD0kZOShY9AAmAGYAFgBWADYAdgAOAE5MWIG6YZRRlamZ7o0sBalZaFXN3cPTzABCC6AA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 85 | 86 | SUs, along with DUs, Record Types and Option, allow you to represent any model with the business rules encoded into the types themselves. It can help us avoid bugs and it also helps point us down a path where we can see up front what we must account for. When you come back to your code in 6+ months you know exactly what you are expecting of that type, no matter how deep into the codebase you are. You won't need to refresh your mind as to what the domain rule around the string value of property X is. Is it always there? Does it matter if it's null or not? 87 | 88 | You can do all of the above in C# of course, but the amount of code required to do it is verbose, hard to maintain and easy to have bugs creep in. That's not to say that you shouldn't! In fact I recommend being more explicit with your type modelling in C#, but be warned it can become tiring quickly due to the amount of code you need to write for it. In F# you would happily create these more constrained types for as many items as you wish, in C# I feel like I must pick my battles wisely and save energy for another day. 89 | 90 | _I have had to re-write this lesson because I was using our internal terminology and context, I'm not entirely happy with how this version has turned out, and may come back to it in the future. Hopefully the contrived examples above give some input and food for thought._ -------------------------------------------------------------------------------- /Lessons/day-19-pattern-matching-part-4.md: -------------------------------------------------------------------------------- 1 | # Day 19: Active Patterns (Pattern Matching part 4) 2 | 3 | Let's look at a more common Active Pattern, similar to the the Single-case Active Pattern but with a twist. The Single-case Partial is very similar to the Single-case Pattern, except that it allows for the scenario where you can't transform the input to the output. Hence it has the signature of ```'T -> Option<'A>``` 4 | 5 | A good example would be converting a string to an integer: 6 | 7 | ```fsharp 8 | let (|Int|_|) (input: string) = 9 | match System.Int32.TryParse input with 10 | | true, value -> Some value 11 | | _ -> None 12 | ``` 13 | 14 | An obvious difference when compared to the Single-case Pattern is the _ in the label list. The label between the first two pipes represents the `Some` outcome, and the `_` represents the `None` outcome. Within the function body you then use `Some` and `None` to provide the two outcomes. 15 | 16 | When you come to use the Single-case partial in a match you do not need to worry about stating `Some` / `None` yourself. F# unwraps this for you. If it is Some the label will match and you will be expected to use the variable. For the none scenario a match is not found, and you are required to handle the match through another branch: 17 | 18 | ```fsharp 19 | let printInt input = 20 | match input with 21 | | Int i -> printf "%d" i 22 | | _ -> printf "`%s` is not valid integer" input 23 | printInt "99" 24 | printInt "99.9" 25 | ``` 26 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAFAPgkgOzDA+jAlNAlggDgVzAC4IBnMAJxwHNMBeAWACgJW3WBbAQzAGMALCAGUAnuRAcAdIjABmAEySAKhREAFLhVIgIOApADuWMP2bt2MCJXwgANBABuXYDYgBaAHzCA9hx1OXEDNzVksUdy8AOW8EIKZmUEhcKiQZXTxCCEYWNm4+QT1MoxNg0Ig0rAiIZJwwADMIACIAUgATRt1SiDCqmqQGxoADZtJB3VIIBG9IAKxW9LAQahAKDsKwZmY+sDTGgE49xq2UnaQmg8lDoA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 27 | 28 | As you can see we make use of our Int active pattern to get the i variable, then print that value out. When Int isn't matched we use a wildcard to decide what to do instead. 29 | 30 | Feel free to play around with the above. It's good to remember that although I showed you the pattern matching techniques one at a time, but they can be combined in many ways. Here's a few examples of using the active pattern with some alternative pattern matching techniques: 31 | 32 | ```fsharp 33 | let printInt input = 34 | match input with 35 | | Int 42 -> printf "The answer to everything" // Constant pattern 36 | | Int _ -> printf "All I know is that it's a number" // Discard the output 37 | | _ -> printf "`%s` is not valid integer" input 38 | ``` 39 | 40 | We can run Single-case partial active patterns one after another, so we'll write a few more Single-case partial patterns and use them one after another. One thing that you'll probably notice is that even though you can't see the implementation, you immediately understand what the below is doing. You can see the function definitions via the link. 41 | 42 | ```fsharp 43 | let printType input = 44 | match input with 45 | | Int i -> printf "Int: %d" i 46 | | Decimal d -> printf "Decimal: %f" d 47 | | Guid g -> printf "Guid: %A" g 48 | | Bool b -> printf "Bool: %b" b 49 | | _ -> printf "Cannot parse into known type `%s`" input 50 | ``` 51 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAFAPgkgOzDA+jAlNAlggDgVzAC4IBnMAJxwHNMBeAWACgJWIBbAQzAGMALCAGUAnuRDsAdIjABmAEwSAKhWEAFThVIgIOApADuWMH2ZsIMCJXwgANBABunYNYgBaAHxCA9u22PnIKZsFihungByXgiBTMygkLAAIiA8WFzAqBjYeIQk5FQItBCMLGxcvAIiYpLJqelKKuqa2rqEEIbGQawWVrYOTi4e3r79AV3mEKFDkdHMceDQMADi+FgAJpmYUK3EZJQ09OPl-EKiYOISK+sNahpaOjkGRial3ZYU1nb+g56CPn4DGJmEJhCAzGLzBIwABCXi8GTQWx2eX2hUOrw43BOOyUXgAMl59CAKFBMB0XsDoAAiACMVJgVN6VMwoL+I164wsUCpAAZ6VSAGZOLTM1n-CBC4BaTkQKlUsXsj5A4KTUHguZMeIQXAFMCKYS4FqPYpHLECHbtZ4y6Q6UE6nBgAWy6QkACka3lWBltTSTggaztuqdVJ96TdAvlaxlVwD1EDDuDMbdAEF5dQZbD4RAAEbxpDBzPAN3Z+XZmVTTz2-OygDCnAQCC8kFwdyNYC8EAA1o39AhLAbtAADV2kQeex4aqt6geygCcs6pzCn+sNc9nEgXS91K+0VM4PBkAHY5CAZDxXHIeMfXAAWABsa3PnDvApvrlnaxpApSd4ArCfszpLcHR3WUmWApBQKpABVBAsB4LwKD7AAJRCECpIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 52 | 53 | Now onto the Paramaterized partial. It has the same purpose as the Single-case Partial Pattern, the difference this time is that we can provide it with one or more parameters. This is hard to represent as a single functional signature, so I'll show you a few examples 54 | 55 | ```fsharp 56 | 'T -> 'P -> Option<'A> // Single additional parameter 57 | 'T -> 'P1 -> 'P2 -> Option<'A> // Two additional parameters 58 | 'T -> 'P1 -> 'P2 -> 'P3 -> Option<'A> // Three additional parameters 59 | 'T -> 'P1 ... 'PN -> Option<'A> // The ... represents any number of parameters 60 | ``` 61 | 62 | Let's re-use our Int Single-case Partial pattern, and create a Parameterized Partial. 63 | 64 | ```fsharp 65 | let (|IntGreaterThan|_|) i input = 66 | match input with 67 | | Int value when value > i -> Some value 68 | | _ -> None 69 | ``` 70 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAFAPgkgOzDA+jAlNAlggDgVzAC4IBnMAJxwHNMBeAWACgJWIBbAQzAGMALCAGUAnuRDsAdIjABmAEwSAKhWEAFThVIgIOApADuWMH2ZsIMCJXwgANBABunYNYgBaAHxCA9u22PnIKZsFihungByXgiBTMygkLDSAOIUINwgFIp8nAioGDo6eIQQjCxsXLwCusWGxkGsFtIOTi76fCAIzQEQnlhh3r5d1vXmEKEeEJHRzHHgEHxe+gAyGtTa1ZClZhX8hXoQtSZlDRDJqemZ2Z1yAAwFE7hUSABmEABEAKQAJjqkENTnMAZSxXCC3N46EaNJApNJAy45CAARjufQeTzAr0+PywfwBcOBxkRKIhWChY36jxwmPecEg7Cw1D4kAARtpEdS7CzikYAOR-L4gZ44IwgYDCCAILyQfEXEHEm5vGZMBbLVbaN4ATk1SpVixWFDW7yROuYqoNRreAFUEFgeF4KJ0ABIOhBvIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 71 | 72 | I think the above is fairly self explanatory, it's not a big leap from where we were before. You may not have noticed but we dropped the type definition from the input, that was possible because we are using the Int pattern, which states the type and thus the compiler can now infer what we're expecting. 73 | 74 | Finally, we have the Multiple-case pattern. As you have probably guessed, this pattern allows you to define a finite list of labels that the pattern will return. It works in a very similar way to matching on a Discriminated Union. Once you start to pattern match on one of the Multiple-case labels, the compiler will remind you whenever you have not taken care of all the labels. 75 | 76 | I'll re-use our previous type Single-case partial patterns and create a Multiple-case pattern, which I've placed in a module to help avoid the clashing of pattern names: 77 | 78 | ```fsharp 79 | module Param = 80 | let (|Int|Decimal|Guid|Bool|String|) input = 81 | match input with 82 | | Int i -> Int i 83 | | Decimal d -> Decimal d 84 | | Guid g -> Guid g 85 | | Bool b -> Bool b 86 | | _ -> String input 87 | let parameterType input = 88 | match input with 89 | | Param.Int i -> printf "Int: %d" i 90 | | Param.Decimal d -> printf "Decimal: %f" d 91 | | Param.Guid g -> printf "Guid: %A" g 92 | | Param.Bool b -> printf "Bool: %b" b 93 | | Param.String str -> printf "String: %s" str 94 | ``` 95 | [Try the code](https://try.fsharp.org/#?code=DYUwLgBAFAPgkgOzDA+jAlNAlggDgVzAC4IBnMAJxwHNMBeAWACgJWIBbAQzAGMALCAGUAnuRDsAdIjABmAEwSAKhWEAFThVIgIOApADuWMH2ZsIMCJXwgANBABunYNYgBaAHxCA9u22PnIKZsFihungByXgiBTMygkLAAIiA8WFzAqBjYeIQk5FQItBCMLGxcvAIiYpLJqelKKuqa2rqEEIbGQawWVrYOTi4e3r79AV3mEKFDkdHMceDQMADi+FgAJpmYUK3EZJQ09OPl-EKiYOISK+sNahpaOjkGRial3ZYU1nb+g56CPn4DGJmEJhCAzGLzBIwABCXi8GTQWx2eX2hUOrw43BOOyUXgAMl59CAKFBMB0XsDoAAiACMVJgVN6VMwoL+I164wsUCpAAZ6VSAGZOLTM1n-CBC4BaTkQKlUsXsj5A4KTUHguZMdheNb4UAQJqcdjFcbxRbSGC1NJOZarDaw+EwQSo6hZHbGjFmY4CN3k8aU6Q6UEBrB+lWW9IQNag8NOSOht5XKPUUGJiDUeMTe3ACAAI1BWdzGZBQydBWTOw1ptwGkN4GJimEuBaj3dnqx3pbvoxFgNkmDoNwBTAAtl0hIAFI1vKQ939TWaikrdmo0NBzhh7KY8AJwL5WsZb3Lra0wOhyOqYmJwBBeXp2eHgt51dn2VZic5+U5g-ziSlmh7ChT3Xc8-0KCdSHlfINWrCha3OCgGybWUAE5kKpZgYLg+tG20KlUIkNCMPnOsEJw2VOB4GQAHY5BAGQeFcOQeBo1wABYADY1gYzh2IFVjXGQtYaQFFJ2IAVlonM6SI2DfHgxDcKZGSsNIpCqQAVQQLAeC8CgEAgAAJXSECpIA&html=DwCwLgtgNgfAsAKAAQqaApgQwCb2ag4CdMTJcMABwFp0BHAVwEsA3AXgCIBhAewDsw6AdQAqAT0roOSAMb9BAzoIAeYAPThoAbhkhMAJwDOJNgzAAzagA4OeQhqy5EhAEY9sYu6mBq3HvD6asEA&css=Q) 96 | 97 | If you jump into the example you'll see the function being run, and the printed values and their types. If you removed anyone of the matching lines from the parameterType function, you'll notice that the compiler will warn you. Hopefully you remember that this is the same behaviour as when pattern matching a discriminated union. 98 | 99 | I'm not sure what your thoughts on active patterns are, perhaps you see their power or maybe like me you think they are clever but can't yet see a use. You may find them hard to read at first, but keep practicing and that will come. 100 | 101 | The fact is that they are an invaluable tool. It may take a few times of looking at them and going back to them a few times after that before realising, but keep at it because it is worth the effort. 102 | 103 | Today concludes what I would consider the building blocks F#. You haven't been shown everything in the subjects that we have covered, but I think you've seen enough to understand most of the syntax you would see when reading most F# code. 104 | 105 | Hopefully the last four weeks have opened your eyes to some benefits that F# brings. --------------------------------------------------------------------------------