├── .gitignore ├── Applying_and_Evaluating_Functional_Programming_Paradigms_and_Techniques_in_Developing_Games.pdf ├── PracticalLanguageExt.pdf ├── Program.cs ├── README.md ├── The Language Ext Tutorial.pdf ├── Tutorial01_monad ├── Box.cs ├── Program.cs └── Tutorial01 - Monad.csproj ├── Tutorial02_transformations_1 ├── Box.cs ├── Program.cs └── Tutorial02 - Transformations 1.csproj ├── Tutorial03_pipelines ├── Box.cs ├── Program.cs └── Tutorial03 - Pipelines.csproj ├── Tutorial04_methods_1 ├── Box.cs ├── Program.cs └── Tutorial04 - Methods 1.csproj ├── Tutorial05_methods_2 ├── Box.cs ├── Program.cs └── Tutorial05 - Methods 2.csproj ├── Tutorial06_Linq ├── Box.cs ├── Program.cs └── Tutorial06 - Linq.csproj ├── Tutorial07_transformations_2 ├── Box.cs ├── Program.cs └── Tutorial07 - Transformations 2.csproj ├── Tutorial08_declarative_style ├── Box.cs ├── Program.cs └── Tutorial08 - Declarative style.csproj ├── Tutorial09_automatic_validation ├── Box.cs ├── Program.cs └── Tutorial09 - automatic validation.csproj ├── Tutorial10_returning_monads ├── Box.cs ├── Program.cs └── Tutorial10 - returning monads.csproj ├── Tutorial11_short_circuiting ├── Box.cs ├── Program.cs └── Tutorial11 - short circuiting.csproj ├── Tutorial12_function_compositionial12 ├── Box.cs ├── Program.cs └── Tutorial12 - function composition.csproj ├── Tutorial13_pure_functions ├── Box.cs ├── Program.cs └── Tutorial13 - pure functions.csproj ├── Tutorial14_Intro_to_Eithers ├── Program.cs └── Tutorial14 - Intro to Eithers.csproj ├── Tutorial15_BiBind ├── Program.cs └── Tutorial15 - BiBind().csproj ├── Tutorial16_BiExists ├── Program.cs └── Tutorial16 - BiExists().csproj ├── Tutorial17_Fold ├── Program.cs └── Tutorial17 - Fold().csproj ├── Tutorial18_Iter ├── Program.cs └── Tutorial18 - Iter().csproj ├── Tutorial19_BiMap ├── Program.cs └── Tutorial19 - BiMap().csproj ├── Tutorial20_BindLeft ├── Program.cs └── Tutorial20 - BindLeft().csproj ├── Tutorial21_Match ├── Program.cs └── Tutorial21 Match().csproj ├── Tutorial22_BiMapT_and_MapT ├── Program.cs └── Tutorial22 - BiMapT() and MapT().csproj ├── Tutorial23_BindT ├── Program.cs └── Tutorial23 - BindT().csproj ├── Tutorial24_IterT ├── Program.cs └── Tutorial24 - IterT().csproj ├── Tutorial25_Apply ├── Program.cs └── Tutorial25 - Apply().csproj ├── Tutorial26_Partition ├── Program.cs └── Tutorial26 - Partition().csproj ├── Tutorial27_Match ├── Program.cs └── Tutorial27 - Match().csproj ├── Tutorial28_Intro_to_Option ├── Program.cs └── Tutorial28 - Intro to Option.csproj ├── Tutorial29_Option_Basics ├── Program.cs └── Tutorial29 - Option Basics.csproj ├── Tutorial30_Option_in_functions ├── Program.cs └── Tutorial30 - Option in functions.csproj ├── Tutorial31_IfSome_and_IfNone ├── Program.cs └── Tutorial31 - IfSome() and IfNone().csproj ├── Tutorial32_Pipelining_with_Optionsial32 ├── Program.cs └── Tutorial32 - Pipelining with Options.csproj ├── Tutorial33_ToEither ├── Program.cs └── Tutorial33 - ToEither.csproj ├── Tutorial34_BiMap() ├── Program.cs └── Tutorial34 - BiMap().csproj ├── Tutorial35_ThrowIfFailed() ├── CustomExtensions.cs ├── IAmFailure.cs ├── Program.cs └── Tutorial35 - ThrowIfFailed().csproj ├── Tutorial36_FailureToNone ├── CustomExtensions.cs ├── IAmFailure.cs ├── Program.cs └── Tutorial36 - FailureToNone.csproj ├── Tutorial37_Caching_and_Memoization ├── Program.cs └── Tutorial37 - Caching and Memoization.csproj ├── Tutorial38_Changing_state_over_time ├── ChangeAgeEvent.cs ├── ChangeExpertiseEvent.cs ├── ChangeNameEvent.cs ├── ChangeRoleEvent.cs ├── Event.cs ├── Person.cs ├── Program.cs └── Tutorial38 - Changing state over time.csproj ├── Tutorial39_Immutability_Smart_Constructors ├── Person.cs ├── PersonExtensions.cs ├── Program.cs └── Tutorial39 - Immutability Smart Constructors.csproj ├── Tutorial40_Try ├── Extensions.cs ├── ExternalLibraryFailure.cs ├── IAmFailure.cs ├── Program.cs └── Tutorial40 - Try.csproj ├── Tutorial41_Statics ├── Failures │ ├── AggregatePipelineFailure.cs │ ├── ConditionNotSatisfiedFailure.cs │ ├── ExceptionFailure.cs │ ├── ExternalLibraryFailure.cs │ ├── IAmFailure.cs │ ├── InvalidDataFailure.cs │ ├── NotFound.cs │ ├── NotTypeExceptionFailure.cs │ ├── ShortCircuitFailure.cs │ ├── TransformExceptionFailure.cs │ ├── UnexpectedFailure.cs │ ├── UnexpectedFailureException.cs │ └── UninitializedFailure.cs ├── Program.cs ├── Statics.cs └── Tutorial41 - Dealing with errors.csproj ├── UnderstandingLanguageExt.sln ├── UnderstandingLanguageExtTutorial.csproj ├── make.sh ├── myMediaFolder └── media │ ├── image10.png │ ├── image101.png │ ├── image103.png │ ├── image105.png │ ├── image107.png │ ├── image109.png │ ├── image111.png │ ├── image113.png │ ├── image115.png │ ├── image117.png │ ├── image119.png │ ├── image12.png │ ├── image121.png │ ├── image123.png │ ├── image125.png │ ├── image127.png │ ├── image14.png │ ├── image15.png │ ├── image17.png │ ├── image18.png │ ├── image2.png │ ├── image20.png │ ├── image23.png │ ├── image25.png │ ├── image27.png │ ├── image29.png │ ├── image31.png │ ├── image33.png │ ├── image35.png │ ├── image37.png │ ├── image39.png │ ├── image4.png │ ├── image41.png │ ├── image43.png │ ├── image45.png │ ├── image48.png │ ├── image49.png │ ├── image51.png │ ├── image53.png │ ├── image55.png │ ├── image57.png │ ├── image59.png │ ├── image6.png │ ├── image61.png │ ├── image63.png │ ├── image65.png │ ├── image67.png │ ├── image69.png │ ├── image71.png │ ├── image73.png │ ├── image75.png │ ├── image77.png │ ├── image79.png │ ├── image8.png │ ├── image81.png │ ├── image83.png │ ├── image85.png │ ├── image87.png │ ├── image89.png │ ├── image91.png │ ├── image93.png │ ├── image95.png │ ├── image97.png │ └── image99.png ├── rename.bat ├── tutorial.md └── tutorial1.md /Applying_and_Evaluating_Functional_Programming_Paradigms_and_Techniques_in_Developing_Games.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/Applying_and_Evaluating_Functional_Programming_Paradigms_and_Techniques_in_Developing_Games.pdf -------------------------------------------------------------------------------- /PracticalLanguageExt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/PracticalLanguageExt.pdf -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LanguageExtForNewDevelopersTutorial 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | return; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /The Language Ext Tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/The Language Ext Tutorial.pdf -------------------------------------------------------------------------------- /Tutorial01_monad/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Runtime.InteropServices.ComTypes; 5 | 6 | namespace Tutorial01 7 | { 8 | /// 9 | /// A box can hold a thing only 10 | /// 11 | /// The type of the thing 12 | public class Box 13 | { 14 | public Box(T newItem) 15 | { 16 | Item = newItem; 17 | IsEmpty = false; 18 | } 19 | 20 | public Box() { } 21 | 22 | private T _item; 23 | 24 | public T Item 25 | { 26 | get => _item; 27 | set 28 | { 29 | _item = value; 30 | IsEmpty = false; 31 | } 32 | } 33 | 34 | public bool IsEmpty = true; 35 | } 36 | 37 | public static class BoxMethods 38 | { 39 | /// 40 | /// Transforms the contents of a Box, in a user defined way 41 | /// 42 | /// The type of the thing in the box to start with 43 | /// The result type that the transforming function to transform to 44 | /// The Box that the extension method will work on 45 | /// User defined way to transform the contents of the box 46 | /// The results of the transformation, put back into a box 47 | public static Box Select(this Box box, Func map) 48 | { 49 | // Validate/Check if box is valid(not empty) and if so, run the transformation function on it, otherwise don't 50 | if (box.IsEmpty) 51 | { 52 | // No, return the empty box 53 | return new Box(); 54 | } 55 | 56 | // Extract the item from the Box and run the provided transform function ie run the map() function 57 | // ie map is the name of the transformation function the user provided. 58 | TB transformedItem = map(box.Item); 59 | 60 | return new Box(transformedItem); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Tutorial01_monad/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tutorial01 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | // A Box, can hold any type but in this case it holds and makes provision for an int. 10 | // Look at the implementation of the Box, notice its just a C# Class I've created. 11 | Box myNumberBox1 = new Box(1); 12 | 13 | // I can look into the Box 14 | Console.WriteLine($"The contents of my NumberBox is initially is '{myNumberBox1.Item}'"); 15 | 16 | // But this Box is different. It can be used in a Linq Expression. 17 | // This is because it has a special function defined for it called Select(). 18 | // Have a look at Box class again, check out the Select() extension method at the bottom. 19 | // This single function allows the following Linq usages: 20 | 21 | // Fetch or Select() a value from the box by using the Box's Select() function implicitly 22 | var result = from number1 in myNumberBox1 23 | select number1 + 1; // This is called the 'Linq Expression Syntax' and requires Box to have a Select() function for it to work 24 | 25 | // This is called the Linq Fluent syntax - does the same thing as the above 26 | Box result2 = myNumberBox1.Select(x => x + 1); // x=> x+1 is the transformation function also known as a mapping function 27 | 28 | // Have a Look at Box class again, and specifically the Select() extension method again and try and understand what its doing. 29 | // Hint: Its doing two things 1) allowing you to pass a function to it that it will run for the item in the box 2) only doing 1) if 30 | // the contents of the box is valid (so it does some validation) 31 | 32 | Console.WriteLine($"The number is now after having passed a mapping function1 to it results in its contents being '{result.Item}'"); 33 | Console.WriteLine($"The number is now after having passed a mapping function2 to it results in its contents being '{result2.Item}'"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tutorial01_monad/Tutorial01 - Monad.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial0 - monad 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial02_transformations_1/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial01 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// The type of the thing 14 | public class Box 15 | { 16 | public Box(T newItem) 17 | { 18 | Item = newItem; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _item; 25 | 26 | public T Item 27 | { 28 | get => _item; 29 | set 30 | { 31 | _item = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | 38 | 39 | } 40 | 41 | public static class BoxMethods 42 | { 43 | /// 44 | /// Validate, Extract, Transform and Lift (If Valid) 45 | /// 46 | public static Box Select(this Box box, Func map) 47 | { 48 | // Validate 49 | if (box.IsEmpty) 50 | return new Box(); 51 | 52 | // Extract 53 | var extracted = box.Item; 54 | 55 | // Transform 56 | TB transformedItem = map(extracted); 57 | 58 | // Lift 59 | return new Box(transformedItem); 60 | } 61 | 62 | /// 63 | /// Validate, Extract, Transform and Lift (If Valid) 64 | /// Check/Validate then transform to T and lift into Box 65 | /// 66 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 67 | { 68 | // Validate 69 | if(box.IsEmpty) 70 | return new Box(); 71 | 72 | //Extract 73 | TA extract = box.Item; 74 | 75 | // Transform and the user-defined function (notice that its up to the user defined function to 'lift' any result of the transformation into a new Box) 76 | Box transformedAndLifted = bind(extract); // should return its results of its transformation in a Box 77 | 78 | return transformedAndLifted; 79 | } 80 | 81 | /// 82 | /// Validate, Extract, Transform and automatic Lift (If Valid) 83 | /// 84 | public static Box Map(this Box box, Func select /*Transform*/) 85 | { 86 | // Validate 87 | if(box.IsEmpty) 88 | return new Box(); 89 | 90 | // Extract 91 | TA extract = box.Item; 92 | 93 | // Transform 94 | TB transformed = select(extract); // user provided function does not need to 'lift' its result into a Box like Bind() requires 95 | 96 | // Lift 97 | return new Box(transformed); 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Tutorial02_transformations_1/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Tutorial01; 3 | 4 | namespace Tutorial02 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | // Now my Box contains another kind of thing, a list of integers. So effectively my Box is a box of numbers! 11 | Box numbers1 = new Box(new []{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); 12 | Box numbers2 = new Box(new[] { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }); 13 | 14 | // Now have a look at Box class again, it has changed a little with two new extension methods. 15 | 16 | // Notice how the it (the extension methods) follows an interesting trend of doing VETL or Validate content, Extract Content, Transform Content and Lift the content 17 | 18 | // Validate, Extract, Transform and YouLift(If Valid) 19 | Box transformedResult1 = numbers1.Bind(contents => MyFunction(contents)); // user define transformation passed to bind(), this currently just ignores the extracted contents in the Box and returns a new set of numbers as dictated by MyFunction() 20 | 21 | // Validate, Extract, Transform and automatic Lift (If Valid) 22 | Box transformedResult2 = numbers2.Map(contents => MyFunction2(contents)); // same transformation result, but we our transformation function didn't 23 | // have to return a Box (we used map to run our transformation which automatically will put the result of our transform function in a new Box) 24 | 25 | // The Box class is now considered a Monad, because it has these two additional functions defined on it (map and bind). 26 | // Note in both cases of Bind() and Map() we did a transformation of the contents ie we provided a function that would work on the item in the Box(or Monad) 27 | 28 | // In both cases we didn't doing anything with the contents of the box, so we didn't really transform the contents(we just used what Map() and Bind() extracted from the box) 29 | // We could have included the contents in our transformation and manipulated it... 30 | 31 | // The take away is that Map() and Bind() do the same thing but Bind() requires you to put your transformation result back in the Box, while Map doesn't. 32 | // Both methods 'manage' your user defined transform function by running it only if it deems it should (validation passes) and then depending on the specific function, 33 | // it will either lisft the result of the transformation(map) or require that your transformation function's signature explicitly says it will it it itself (bind) 34 | 35 | // This means, with a you dont have to return a Box (like you do when you with Bind), when transforming with the Map() function...it automatically does this for you 36 | 37 | Console.WriteLine($"The result of the Bind operation was {transformedResult1.Item}"); 38 | Console.WriteLine($"The result of the Map operation was {transformedResult2.Item}"); 39 | 40 | // But here is something sneaky: 41 | Box transformedResult3 = numbers1.Map(contents => "I'm a string!"); // Look, we've been able to change the type of the Box from a int[] to a string! 42 | // This is by virtue of the fact that its what your transformation function returned, and it transformed the return type also...and put it back into a box 43 | // Its still in a Box, but is a box of a string now instead of a box of numbers.... Map() and Bind() can do this, and this is what makes these function really at transformations 44 | } 45 | 46 | private static int[] MyFunction2(int[] numbers) 47 | { 48 | // Used as a map() transformation, so no need to lift into a Box or anything... 49 | return new int[] {3, 4, 5}; 50 | } 51 | 52 | private static Box MyFunction(int[] integerArray) 53 | { 54 | // Notice that this function if its going to be used in Box's Bind() function (and is indeed compatible with it - see Bind function signature), needs to return a new Box ie lift into a Box() again 55 | return new Box(new int[] {1, 2}); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tutorial02_transformations_1/Tutorial02 - Transformations 1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial02\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial03_pipelines/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | // This box has not changed its meaning since the last tutorial 9 | namespace Tutorial01 10 | { 11 | /// 12 | /// A box can hold 1 thing only 13 | /// 14 | /// The type of the thing 15 | public class Box 16 | { 17 | public Box(T newItem) 18 | { 19 | Item = newItem; 20 | IsEmpty = false; 21 | } 22 | 23 | public Box() { } 24 | 25 | private T _item; 26 | 27 | public T Item 28 | { 29 | get => _item; 30 | set 31 | { 32 | _item = value; 33 | IsEmpty = false; 34 | } 35 | } 36 | 37 | public bool IsEmpty = true; 38 | 39 | 40 | } 41 | 42 | public static class BoxMethods 43 | { 44 | /// 45 | /// Validate, Extract, Transform and Lift (If Valid) 46 | /// 47 | public static Box Select(this Box box, Func map) 48 | { 49 | // Validate 50 | if (box.IsEmpty) 51 | return new Box(); 52 | 53 | // Extract 54 | var extracted = box.Item; 55 | 56 | // Transform 57 | TB transformedItem = map(extracted); 58 | 59 | // Lift 60 | return new Box(transformedItem); 61 | } 62 | 63 | /// 64 | /// Validate, Extract, Transform and Lift (If Valid) 65 | /// Check/Validate then transform to T and lift into Box 66 | /// 67 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 68 | { 69 | // Validate 70 | if(box.IsEmpty) 71 | return new Box(); 72 | 73 | //Extract 74 | TA extract = box.Item; 75 | 76 | // Transform and lift 77 | Box transformedAndLifted = bind(extract); 78 | 79 | return transformedAndLifted; 80 | } 81 | 82 | /// 83 | /// Validate, Extract, Transform and automatic Lift (If Valid) 84 | /// 85 | public static Box Map(this Box box, Func select /*Transform*/) 86 | { 87 | // Validate 88 | if(box.IsEmpty) 89 | return new Box(); 90 | 91 | // Extract 92 | TA extract = box.Item; 93 | 94 | // Transform 95 | TB transformed = select(extract); 96 | 97 | // Lift 98 | return new Box(transformed); 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /Tutorial03_pipelines/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Tutorial01; 3 | 4 | namespace Tutorial03 5 | { 6 | // We continue working on our Monad type, Box. 7 | // Showing how short-circuiting works when chaining or cascading multiple Bind() or Map() operations 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | // Before working further on out Monad Box, read this : 13 | // Why do we actually need Monads? Why do we have to have Map() and Bind() 14 | // Here is why? https://stackoverflow.com/questions/28139259/why-do-we-need-monads 15 | 16 | Box numberHolder = new Box(25); 17 | Box stringHolder = new Box("Twenty Five"); 18 | 19 | //Validate, Extract, Transform, lift 20 | Box result1 = numberHolder.Map(i => "Dude Ive Been Transformed To a string automatically"); 21 | 22 | // Transform the contents of the box by passing it down a series of Bind()s 23 | // so there are multiple associated VETL->VETL->VETL steps that represents the Binds() 24 | // effectively representing a pipeline of data going in and out of Bind(VETL) functions 25 | Box result2 = stringHolder 26 | .Bind(s => new Box(2)) //Validate Extract Transform lift 27 | .Bind(i => new Box()) // Validate step only 28 | .Bind(i => new Box("hello")); // Validate step only 29 | 30 | // Remember that the 'validate' step is implicit and is actually coded directly into the Bind() or Map() functions. 31 | // The validity of a box in this case, is if it is empty or not(look at the Bind functions' code that explicitly checks this), 32 | // It is empty(invalid) it will not run the the user provided transformation function, otherwise it will. 33 | 34 | // This is an example of 'short-circuiting' out-of the 35 | // entire set of transformations early. So if the First Bind short-circuits, the next Binds will too and so they will not run 36 | 37 | Console.WriteLine($"The contents of result 2 is {result2.Item}"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tutorial03_pipelines/Tutorial03 - Pipelines.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial03\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial04_methods_1/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial01 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// The type of the thing 14 | public class Box 15 | { 16 | public Box(T newItem) 17 | { 18 | Item = newItem; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _item; 25 | 26 | public T Item 27 | { 28 | get => _item; 29 | set 30 | { 31 | _item = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | 38 | 39 | } 40 | 41 | public static class BoxMethods 42 | { 43 | /// 44 | /// Validate, Extract, Transform and Lift (If Valid) 45 | /// 46 | public static Box Select(this Box box, Func map) 47 | { 48 | // Validate 49 | if (box.IsEmpty) 50 | return new Box(); 51 | 52 | // Extract 53 | var extracted = box.Item; 54 | 55 | // Transform 56 | TB transformedItem = map(extracted); 57 | 58 | // Lift 59 | return new Box(transformedItem); 60 | } 61 | 62 | /// 63 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 64 | /// 65 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 66 | { 67 | // Validate 68 | if(box.IsEmpty) 69 | return new Box(); 70 | 71 | // Extract 72 | var extract = box.Item; 73 | 74 | // Transform and LiftTo 75 | Box liftedResult = bind(extract); 76 | 77 | if(liftedResult.IsEmpty) 78 | return new Box(); 79 | 80 | // Project/Combine 81 | TC t2 = project(extract, liftedResult.Item); // This forms the select xzy in the Linq Expression in the tutorial. 82 | return new Box(t2); 83 | } 84 | 85 | /// 86 | /// Validate, Extract, Transform and Lift (If Valid) 87 | /// Check/Validate then transform to T and lift into Box 88 | /// 89 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 90 | { 91 | // Validate 92 | if(box.IsEmpty) 93 | return new Box(); 94 | 95 | //Extract 96 | TA extract = box.Item; 97 | 98 | // Transform and lift 99 | Box transformedAndLifted = bind(extract); 100 | 101 | return transformedAndLifted; 102 | } 103 | 104 | /// 105 | /// Validate, Extract, Transform and automatic Lift (If Valid) 106 | /// 107 | public static Box Map(this Box box, Func select /*Transform*/) 108 | { 109 | // Validate 110 | if(box.IsEmpty) 111 | return new Box(); 112 | 113 | // Extract 114 | TA extract = box.Item; 115 | 116 | // Transform 117 | TB transformed = select(extract); 118 | 119 | // Lift 120 | return new Box(transformed); 121 | } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /Tutorial04_methods_1/Tutorial04 - Methods 1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial04\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial05_methods_2/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | // Box has not changed since last tutorial 9 | namespace Tutorial05 10 | { 11 | /// 12 | /// A box can hold 1 thing only 13 | /// 14 | /// The type of the thing 15 | public class Box 16 | { 17 | public Box(T newExtract) 18 | { 19 | Extract = newExtract; 20 | IsEmpty = false; 21 | } 22 | 23 | public Box() { } 24 | 25 | private T _extract; 26 | 27 | public T Extract 28 | { 29 | get => _extract; 30 | set 31 | { 32 | _extract = value; 33 | IsEmpty = false; 34 | } 35 | } 36 | 37 | public bool IsEmpty = true; 38 | 39 | 40 | } 41 | 42 | public static class BoxMethods 43 | { 44 | /// 45 | /// Validate, Extract, Transform and Lift (If Valid) 46 | /// 47 | public static Box Select(this Box box, Func map) 48 | { 49 | // Validate 50 | if (box.IsEmpty) 51 | return new Box(); 52 | 53 | // Extract 54 | var extracted = box.Extract; 55 | 56 | // Transform 57 | TB transformedItem = map(extracted); 58 | 59 | // Lift 60 | return new Box(transformedItem); 61 | } 62 | 63 | /// 64 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 65 | /// 66 | 67 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 68 | { 69 | // Validate 70 | if(box.IsEmpty) 71 | return new Box(); 72 | 73 | // Extract 74 | var extract = box.Extract; 75 | 76 | // Transform and LiftTo 77 | Box liftedResult = bind(extract); 78 | 79 | // Project/Combine 80 | TC t2 = project(extract, liftedResult.Extract); 81 | return new Box(t2); 82 | } 83 | 84 | /// 85 | /// Validate, Extract, Transform and Lift (If Valid) 86 | /// Check/Validate then transform to T and lift into Box 87 | /// 88 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 89 | { 90 | // Validate 91 | if(box.IsEmpty) 92 | return new Box(); 93 | 94 | //Extract 95 | TA extract = box.Extract; 96 | 97 | // Transform and lift 98 | Box transformedAndLifted = bind(extract); 99 | 100 | return transformedAndLifted; 101 | } 102 | 103 | /// 104 | /// Validate, Extract, Transform and automatic Lift (If Valid) 105 | /// 106 | public static Box Map(this Box box, Func select /*Transform*/) 107 | { 108 | // Validate 109 | if(box.IsEmpty) 110 | return new Box(); 111 | 112 | // Extract 113 | TA extract = box.Extract; 114 | 115 | // Transform 116 | TB transformed = select(extract); 117 | 118 | // Lift 119 | return new Box(transformed); 120 | } 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /Tutorial05_methods_2/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Tutorial05; 4 | // Shows various invocations of Bind() and Map() using lambda functions as the user defined transform functions, explicit named functions, and 5 | // using both linq query syntax and fluent forms. 6 | namespace Tutorial05 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | // A Box 13 | Box boxOfIntegers = new Box(new[] { 3, 5, 7, 9, 11, 13, 15 }); 14 | Box boxOfNewIntegers = new Box(new[] { 3, 5, 88, 29, 155, 123, 1 }); 15 | 16 | // Do something with or to the Box, uses user defined function specified in the form of lambdas to the Map() and Bind() functions 17 | 18 | var doubled1 = boxOfIntegers 19 | .Bind(extract => new Box(extract.Select(x => x * 2).ToArray())); // Extract, Validate and transform using Bind() 20 | 21 | var doubled2 = boxOfIntegers 22 | .Map(numbers => numbers.Select(x => x * 2).ToArray()); // Extract, Validate and transform using Bind() 23 | 24 | // Extract, Validate and transform using SelectMany() 25 | var doubled3 = from extract in boxOfIntegers 26 | from transformed in DoubleNumbers(extract) // bind() part of SelectMany() ie transform extracted value 27 | select transformed; // project(extract, transformedAndLiftedResult) part of SelectMany 28 | 29 | var doubled4 = from extract in boxOfIntegers 30 | select DoubleNumbers(extract).Extract; // Use Select via linq expression syntax 31 | 32 | // Note we can use Map or Bind for transformation, but it becomes necessary to choose/use a specific one depending 33 | // on if or not the provided transformation function returns a box or not (lifts or doesn't), 34 | // ie is transformed in a call to Bind() or Map() 35 | Box doubleDouble1 = boxOfIntegers 36 | .Bind(numbers => DoubleNumbers(numbers)) // need to use a transformation function that will lift 37 | .Map(DoubleNumbers) // need to use a transformation that does not already lift 38 | .Bind(box => box.Bind( numbers => DoubleNumbers(numbers) )); // same as above bind() case 39 | 40 | // Using Linq query syntax 41 | var doubleDouble2 = from numbers in boxOfIntegers 42 | from redoubled in DoubleNumbers(numbers) // transformation function needs to lift 43 | select redoubled; // Box's Select() function will do the lift here into Box so no need to in this line 44 | 45 | 46 | // Give me a box of Double Double of my Box 47 | var doubleDouble3 = from firstDoubleTransformation in DoubleMyBox(boxOfIntegers) 48 | from secondDoubleTransformation in DoubleNumbers(firstDoubleTransformation) //VET: bind part of SelectMany() 49 | select secondDoubleTransformation; // project(reDouble, firstDouble) 50 | } 51 | 52 | /// 53 | /// Takes a Box of numbers and produces a box of doubled numbers 54 | /// 55 | private static Box DoubleMyBox(Box boxOfIntegers) 56 | { 57 | return from extract in boxOfIntegers 58 | from doubledNumber in DoubleNumbers(extract) 59 | select doubledNumber; 60 | } 61 | 62 | // transform Extracted, and Lift it 63 | static Box DoubleNumbers(int[] extract) 64 | { 65 | return new Box(extract.Select(x => x * 2).ToArray()); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Tutorial05_methods_2/Tutorial05 - Methods 2.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial05\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial06_Linq/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial06 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// The type of the thing 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | 38 | 39 | } 40 | 41 | public static class BoxMethods 42 | { 43 | /// 44 | /// Validate, Extract, Transform and Lift (If Valid) 45 | /// 46 | public static Box Select(this Box box, Func map) 47 | { 48 | // Validate 49 | if (box.IsEmpty) 50 | return new Box(); 51 | 52 | // Extract 53 | var extracted = box.Extract; 54 | 55 | // Transform 56 | TB transformedItem = map(extracted); 57 | 58 | // Lift 59 | return new Box(transformedItem); 60 | } 61 | 62 | /// 63 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 64 | /// 65 | 66 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) //Notice that the transformation function in SelectMany() is actually called bind() 67 | { 68 | // Validate 69 | if(box.IsEmpty) 70 | return new Box(); 71 | 72 | // Extract 73 | var extract = box.Extract; 74 | 75 | // Transform and LiftTo 76 | Box liftedResult = bind(extract); 77 | 78 | if(liftedResult.IsEmpty) 79 | return new Box(); 80 | 81 | // Project/Combine 82 | TC t2 = project(extract, liftedResult.Extract); 83 | return new Box(t2); 84 | } 85 | 86 | /// 87 | /// Validate, Extract, Transform and Lift (If Valid) 88 | /// Check/Validate then transform to T and lift into Box 89 | /// 90 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 91 | { 92 | // Validate 93 | if(box.IsEmpty) 94 | return new Box(); 95 | 96 | //Extract 97 | TA extract = box.Extract; 98 | 99 | // Transform and lift 100 | Box transformedAndLifted = bind(extract); 101 | 102 | return transformedAndLifted; 103 | } 104 | 105 | /// 106 | /// Validate, Extract, Transform and automatic Lift (If Valid) 107 | /// 108 | public static Box Map(this Box box, Func select /*Transform*/) 109 | { 110 | // Validate 111 | if(box.IsEmpty) 112 | return new Box(); 113 | 114 | // Extract 115 | TA extract = box.Extract; 116 | 117 | // Transform 118 | TB transformed = select(extract); 119 | 120 | // Lift 121 | return new Box(transformed); 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /Tutorial06_Linq/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | /* 5 | This tutorial shows you how pipelining/chaining/cascading is used to call functions. 6 | This tutorial shows how to use the Linq Fluent and Expression syntax to achieve the same thing 7 | This also demonstrates what is a Perfect or valid map and bind function and show when to use map and bind and why 8 | */ 9 | namespace Tutorial06 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | // A Box 16 | Box boxOfIntegers = new Box(new[] { 3, 5, 7, 9, 11, 13, 15 }); 17 | Box boxOfNewIntegers = new Box(new[] { 3, 5, 88, 29, 155, 123, 1 }); 18 | 19 | // Do something with or to the Box 20 | 21 | /* Shown: Using the select many way, ie the linq expression syntax as shown below allows an extracted item from the box, then to be 22 | * passed down a series of transforms by way of those transform functions being compatible with the bind() phase of the SelectMany() function. 23 | 24 | Each can see the prior transformation and can act on it subsequently. 25 | 26 | And as each transformation function is run as part of the SelectMany() implementation of Box, it will also be subject to the VETL phases 27 | which means if the input is not valid, it will return a invalid value and subsequent transforms upon receiving that invalid input will also 28 | return an invalid input and in all those cases i doing that the underlying transform is not run (short-circuiting). 29 | */ 30 | Box doubled1 = from extract in boxOfIntegers // extract items out 31 | from transformed in DoubleNumbers(extract) // bind() part of SelectMany() ie transform extracted value (and below): 32 | from transformed2 in DoubleNumbers(transformed) // use/transform/doublenumbers() the last transformed result 33 | from transformed3 in DoubleNumbers(transformed2) // use/transform/doublenumbers() the last transformed result 34 | from transformed4 in DoubleNumbers(transformed3) // use/transform/doublenumbers() the lsat transformed result 35 | select DoSomethingWith(transformed, transformed2, transformed3, transformed4); // project(extract, transformed) part of SelectMany which always does an automatic result of the projected result (as a box<>) 36 | 37 | Box doubled2 = boxOfIntegers 38 | .Bind(extract => DoubleNumbers(extract)) 39 | .Bind(transformed => DoubleNumbers(transformed)) 40 | .Bind(transformed => DoubleNumbers(transformed)) 41 | .Bind(transformed => DoubleNumbers(transformed)) 42 | .Map(lastTransformedFromAbove => DoSomethingWith(lastTransformedFromAbove)); // I have to use map here because DoSomethingWith() does not return a Box and the result of a Map(will always do that) or Bind must do make its transform function do that 43 | 44 | } 45 | 46 | // Perfect/valid Map function to use with a Map, as map will automatically lift this and so this function does not have to lift its result 47 | private static object DoSomethingWith(params int[][] varargs) 48 | { 49 | // Note we dont have to return a Box<> because as we''ll be running within a SelectMany() expression - it automatically lifts the result in this case a object type 50 | return new object(); 51 | // Note that this function will be acting as the bind() transformation function within the SelectMany() function defined for the Box class (see SelectMany()) 52 | } 53 | 54 | // transform Extracted, and Lift it. 55 | // Perfect bind function to be used in a Bind() because it lifts the result 56 | static Box DoubleNumbers(int[] extract) 57 | { 58 | /* do something with the numbers we extracted from the box and then put them back in the box again 59 | because this function will be run in the bind() phase of the SelectMany() function (see the selectMany function implementation) and that function 60 | requires a signature of : 61 | int[] => Box 62 | */ 63 | return new Box(extract.Select(x => x * 2).ToArray()); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tutorial06_Linq/Tutorial06 - Linq.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial06\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial07_transformations_2/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial07 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// The type of the thing 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | 38 | 39 | } 40 | 41 | public static class BoxMethods 42 | { 43 | /// 44 | /// Validate, Extract, Transform and Lift (If Valid) 45 | /// 46 | public static Box Select(this Box box, Func map) 47 | { 48 | // Validate 49 | if (box.IsEmpty) 50 | return new Box(); 51 | 52 | // Extract 53 | var extracted = box.Extract; 54 | 55 | // Transform 56 | TB transformedItem = map(extracted); 57 | 58 | // Lift 59 | return new Box(transformedItem); 60 | } 61 | 62 | /// 63 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 64 | /// 65 | 66 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 67 | { 68 | // Validate 69 | if(box.IsEmpty) 70 | return new Box(); 71 | 72 | // Extract 73 | var extract = box.Extract; 74 | 75 | // Transform and LiftTo 76 | Box liftedResult = bind(extract); 77 | 78 | // Project/Combine 79 | TC t2 = project(extract, liftedResult.Extract); 80 | return new Box(t2); 81 | } 82 | 83 | /// 84 | /// Validate, Extract, Transform and Lift (If Valid) 85 | /// Check/Validate then transform to T and lift into Box 86 | /// 87 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 88 | { 89 | // Validate 90 | if(box.IsEmpty) 91 | return new Box(); 92 | 93 | //Extract 94 | TA extract = box.Extract; 95 | 96 | // Transform and lift 97 | Box transformedAndLifted = bind(extract); 98 | 99 | return transformedAndLifted; 100 | } 101 | 102 | /// 103 | /// Validate, Extract, Transform and automatic Lift (If Valid) 104 | /// 105 | public static Box Map(this Box box, Func select /*Transform*/) 106 | { 107 | // Validate 108 | if(box.IsEmpty) 109 | return new Box(); 110 | 111 | // Extract 112 | TA extract = box.Extract; 113 | 114 | // Transform 115 | TB transformed = select(extract); 116 | 117 | // Lift 118 | return new Box(transformed); 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /Tutorial07_transformations_2/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | // This shows you how to structure and deal with situations when choosing what function will be used in the bind(), map() functions and how choices you make 5 | // impact the subsequent invocations of those functions when pipe-lining or chaining these functions together 6 | namespace Tutorial07 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | // A Box 13 | Box boxOfIntegers = new Box(new[] { 3, 5, 7, 9, 11, 13, 15 }); 14 | Box boxOfNewIntegers = new Box(new[] { 3, 5, 88, 29, 155, 123, 1 }); 15 | 16 | // Do something with or to the Box 17 | 18 | Box doubled3 = from extract in boxOfIntegers 19 | from transformed in DoubleNumbers(extract) 20 | from transformed2 in DoubleNumbers(transformed) 21 | from transformed3 in DoubleNumbers(transformed2) 22 | from transformed4 in DoubleNumbers(transformed3) 23 | select Dosomethingwith(transformed, transformed2, transformed3, transformed4); 24 | 25 | Box doubleDouble12 = boxOfIntegers 26 | .Bind(extract => DoubleNumbers(extract)) 27 | .Bind(transformed => DoubleNumbers(transformed)) 28 | .Bind(transformed => DoubleNumbers(transformed)) 29 | .Bind(transformed => DoubleNumbers(transformed)) 30 | .Map(transformed => Dosomethingwith(transformed)); // note that because Dosomethingwith() doesn't return a Box, use Map to automatically do that with the result, particularly 31 | // if you ant the result to be in a box becaue perhaps a nother function needs a box as a parameter 32 | 33 | // This shows that you can use either map or bind (they do the same thing ie both transform their input) but map will lift automatically 34 | // and bind() needs its transform function to explicitly do that. 35 | 36 | // This choice of using map or bind will impact on how the subsequent functions require either a lifted(Box) or a non-lifted result. 37 | // So depending on what transformation function you use on the prior input, will affect how the next step deals with either 38 | // a lifted result or a non-lifted result 39 | Box doubleDouble1 = boxOfIntegers 40 | .Bind(numbers => DoubleNumbers(numbers)) // Non automatically lifted result, so DoubleNumbers will have to lift for it to be compatible with Bind(), and it has to lift because subsequent Map or Bind need to work from a lifted value ie a Box<> 41 | .Map(DoubleNumbers) // now we have DoubleNumbers lifting the transformed value and becasue we used Map to transform it, it automatically lifts it again... so we really should have used bind() here but no matter, we'll deal with it: 42 | .Bind(box => box.Bind( numbers => DoubleNumbers(numbers) )) // due to to the double lift, ie Box> we first the extracted item is a Box> which is a Box<> 43 | // so to extract something from that box that i need to use a map or bind, in this case I chose bind, which additionally will not lift and i can use Bind next: 44 | .Bind(numbers => DoubleNumbers(numbers)) 45 | .Bind(numbers => DoubleNumbers(numbers)); 46 | } 47 | 48 | private static object Dosomethingwith(params int[][] varargs) 49 | { 50 | // Note we don't have to return a Box<> because as we''ll be running within a SelectMany() it automatically lifts the result in this case a object type 51 | return new object(); 52 | // Note that this function will be acting as the bind() transformation function within the SelectMany() function 53 | } 54 | 55 | // transform Extracted, and Lift it 56 | static Box DoubleNumbers(int[] extract) 57 | { 58 | /* do something with the numbers we extracted from the box and then put them back in the box again 59 | because this function will be run in the bind() phase of the SelectMany() function (see the selectMany function implementation) and that function 60 | requires a signature of : 61 | int[] => Box 62 | */ 63 | return new Box(extract.Select(x => x * 2).ToArray()); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tutorial07_transformations_2/Tutorial07 - Transformations 2.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial07\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial08_declarative_style/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial08 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// The type of the thing 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | 38 | 39 | } 40 | 41 | public static class BoxMethods 42 | { 43 | /// 44 | /// Validate, Extract, Transform and Lift (If Valid) 45 | /// 46 | public static Box Select(this Box box, Func map) 47 | { 48 | // Validate 49 | if (box.IsEmpty) 50 | return new Box(); 51 | 52 | // Extract 53 | var extracted = box.Extract; 54 | 55 | // Transform 56 | TB transformedItem = map(extracted); 57 | 58 | // Lift 59 | return new Box(transformedItem); 60 | } 61 | 62 | /// 63 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 64 | /// 65 | 66 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 67 | { 68 | // Validate 69 | if(box.IsEmpty) 70 | return new Box(); 71 | 72 | // Extract 73 | var extract = box.Extract; 74 | 75 | // Transform and LiftTo 76 | Box liftedResult = bind(extract); 77 | 78 | if(liftedResult.IsEmpty) 79 | return new Box(); 80 | 81 | // Project/Combine 82 | TC t2 = project(extract, liftedResult.Extract); 83 | return new Box(t2); 84 | } 85 | 86 | /// 87 | /// Validate, Extract, Transform and Lift (If Valid) 88 | /// Check/Validate then transform to T and lift into Box 89 | /// 90 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 91 | { 92 | // Validate 93 | if(box.IsEmpty) 94 | return new Box(); 95 | 96 | //Extract 97 | TA extract = box.Extract; 98 | 99 | // Transform and lift 100 | Box transformedAndLifted = bind(extract); 101 | 102 | return transformedAndLifted; 103 | } 104 | 105 | /// 106 | /// Validate, Extract, Transform and automatic Lift (If Valid) 107 | /// 108 | public static Box Map(this Box box, Func select /*Transform*/) 109 | { 110 | // Validate 111 | if(box.IsEmpty) 112 | return new Box(); 113 | 114 | // Extract 115 | TA extract = box.Extract; 116 | 117 | // Transform 118 | TB transformed = select(extract); 119 | 120 | // Lift 121 | return new Box(transformed); 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /Tutorial08_declarative_style/Tutorial08 - Declarative style.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial08\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial09_automatic_validation/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial09 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// The type of the thing 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | 38 | 39 | } 40 | 41 | public static class BoxMethods 42 | { 43 | /// 44 | /// Validate, Extract, Transform and Lift (If Valid) 45 | /// 46 | public static Box Select(this Box box, Func map) 47 | { 48 | // Validate 49 | if (box.IsEmpty) 50 | return new Box(); 51 | 52 | // Extract 53 | var extracted = box.Extract; 54 | 55 | // Transform 56 | TB transformedItem = map(extracted); 57 | 58 | // Lift 59 | return new Box(transformedItem); 60 | } 61 | 62 | /// 63 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 64 | /// 65 | 66 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 67 | { 68 | // Validate 69 | if(box.IsEmpty) 70 | return new Box(); 71 | 72 | // Extract 73 | var extract = box.Extract; 74 | 75 | // Transform and LiftTo 76 | Box liftedResult = bind(extract); 77 | 78 | // Project/Combine 79 | TC t2 = project(extract, liftedResult.Extract); 80 | return new Box(t2); 81 | } 82 | 83 | /// 84 | /// Validate, Extract, Transform and Lift (If Valid) 85 | /// Check/Validate then transform to T and lift into Box 86 | /// 87 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 88 | { 89 | // Validate 90 | if(box.IsEmpty) 91 | return new Box(); 92 | 93 | //Extract 94 | TA extract = box.Extract; 95 | 96 | // Transform and lift 97 | Box transformedAndLifted = bind(extract); 98 | 99 | return transformedAndLifted; 100 | } 101 | 102 | /// 103 | /// Validate, Extract, Transform and automatic Lift (If Valid) 104 | /// 105 | public static Box Map(this Box box, Func select /*Transform*/) 106 | { 107 | // Validate 108 | if(box.IsEmpty) 109 | return new Box(); 110 | 111 | // Extract 112 | TA extract = box.Extract; 113 | 114 | // Transform 115 | TB transformed = select(extract); 116 | 117 | // Lift 118 | return new Box(transformed); 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /Tutorial09_automatic_validation/Tutorial09 - automatic validation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial09\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial10_returning_monads/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial10 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | 38 | 39 | } 40 | 41 | public static class BoxMethods 42 | { 43 | /// 44 | /// Validate, Extract, Transform and Lift (If Valid) 45 | /// 46 | public static Box Select(this Box box, Func map) 47 | { 48 | // Validate 49 | if (box.IsEmpty) 50 | return new Box(); 51 | 52 | // Extract 53 | var extracted = box.Extract; 54 | 55 | // Transform 56 | TB transformedItem = map(extracted); 57 | 58 | // Lift 59 | return new Box(transformedItem); 60 | } 61 | 62 | /// 63 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 64 | /// 65 | 66 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 67 | { 68 | // Validate 69 | if(box.IsEmpty) 70 | return new Box(); 71 | 72 | // Extract 73 | var extract = box.Extract; 74 | 75 | // Transform and LiftTo 76 | Box liftedResult = bind(extract); 77 | 78 | if(liftedResult.IsEmpty) 79 | return new Box(); 80 | 81 | // Project/Combine 82 | TC t2 = project(extract, liftedResult.Extract); 83 | return new Box(t2); 84 | } 85 | 86 | /// 87 | /// Validate, Extract, Transform and Lift (If Valid) 88 | /// Check/Validate then transform to T and lift into Box 89 | /// 90 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 91 | { 92 | // Validate 93 | if(box.IsEmpty) 94 | return new Box(); 95 | 96 | //Extract 97 | TA extract = box.Extract; 98 | 99 | // Transform and lift 100 | Box transformedAndLifted = bind(extract); 101 | 102 | return transformedAndLifted; 103 | } 104 | 105 | /// 106 | /// Validate, Extract, Transform and automatic Lift (If Valid) 107 | /// 108 | public static Box Map(this Box box, Func select /*Transform*/) 109 | { 110 | // Validate 111 | if(box.IsEmpty) 112 | return new Box(); 113 | 114 | // Extract 115 | TA extract = box.Extract; 116 | 117 | // Transform 118 | TB transformed = select(extract); 119 | 120 | // Lift 121 | return new Box(transformed); 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /Tutorial10_returning_monads/Tutorial10 - returning monads.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial10\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial11_short_circuiting/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial11 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | public override string ToString() 38 | => !IsEmpty ? $"{_extract}" : "The box is empy"; 39 | 40 | } 41 | 42 | public static class BoxMethods 43 | { 44 | /// 45 | /// Validate, Extract, Transform and Lift (If Valid) 46 | /// 47 | public static Box Select(this Box box, Func map) 48 | { 49 | // Validate 50 | if (box.IsEmpty) 51 | return new Box(); 52 | 53 | // Extract 54 | var extracted = box.Extract; 55 | 56 | // Transform 57 | TB transformedItem = map(extracted); 58 | 59 | // Lift 60 | return new Box(transformedItem); 61 | } 62 | 63 | /// 64 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 65 | /// 66 | 67 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 68 | { 69 | // Validate 70 | if(box.IsEmpty) 71 | return new Box(); 72 | 73 | // Extract 74 | var extract = box.Extract; 75 | 76 | // Transform and LiftTo 77 | Box liftedResult = bind(extract); 78 | 79 | if(liftedResult.IsEmpty) 80 | return new Box(); 81 | 82 | // Project/Combine 83 | TC t2 = project(extract, liftedResult.Extract); 84 | return new Box(t2); 85 | } 86 | 87 | /// 88 | /// Validate, Extract, Transform and Lift (If Valid) 89 | /// Check/Validate then transform to T and lift into Box 90 | /// 91 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 92 | { 93 | // Validate 94 | if(box.IsEmpty) 95 | return new Box(); 96 | 97 | //Extract 98 | TA extract = box.Extract; 99 | 100 | // Transform and lift 101 | Box transformedAndLifted = bind(extract); 102 | 103 | return transformedAndLifted; 104 | } 105 | 106 | /// 107 | /// Validate, Extract, Transform and automatic Lift (If Valid) 108 | /// 109 | public static Box Map(this Box box, Func select /*Transform*/) 110 | { 111 | // Validate 112 | if(box.IsEmpty) 113 | return new Box(); 114 | 115 | // Extract 116 | TA extract = box.Extract; 117 | 118 | // Transform 119 | TB transformed = select(extract); 120 | 121 | // Lift 122 | return new Box(transformed); 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Tutorial11_short_circuiting/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | namespace Tutorial11 5 | { 6 | // This tutorial shows how pipe-lining and the way Box's Map() and Bind() functions work, allow for short-circuiting through the validation step in (VETL - Validate, Extract, Transform and Lift) 7 | // and hows why this is useful. 8 | // More specifically it shows how short-circuiting works when you use the Linq Query syntax, and how the underlying implementation of Select() and SelectMany()'s validation 9 | // routines are still used 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | var aBoxOfNumbers1 = new Box( new int[] {1,2,3,4,5 }); 15 | var aBoxOfNumbers2 = new Box(); // empty box 16 | var aBoxOfNumbers3 = new Box( new int[] {6, 7, 8, 9, 10 }); 17 | 18 | // The way Box's validation step works in its Bind/Map functions says thet an empty box is invalid. 19 | // Further more we cannot process any boxes if even one box is empty: 20 | 21 | var result = from number1 in aBoxOfNumbers1 22 | from number2 in aBoxOfNumbers2 // this result causes the next Bind() to check see that its an invalid input and itself returns empty box and this repeats until the result is deemed empty 23 | from number3 in aBoxOfNumbers3 // this does run, but it just bails out at the Validation phase in the bind()'s VETL stage 24 | select number3; 25 | 26 | 27 | Console.WriteLine($"The result is: {result}"); 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tutorial11_short_circuiting/Tutorial11 - short circuiting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial11\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial12_function_compositionial12/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial12 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | public override string ToString() 38 | => !IsEmpty ? $"{_extract}" : "The box is empy"; 39 | 40 | } 41 | 42 | public static class BoxMethods 43 | { 44 | /// 45 | /// Validate, Extract, Transform and Lift (If Valid) 46 | /// 47 | public static Box Select(this Box box, Func map) 48 | { 49 | // Validate 50 | if (box.IsEmpty) 51 | return new Box(); 52 | 53 | // Extract 54 | var extracted = box.Extract; 55 | 56 | // Transform 57 | TB transformedItem = map(extracted); 58 | 59 | // Lift 60 | return new Box(transformedItem); 61 | } 62 | 63 | /// 64 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 65 | /// 66 | 67 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 68 | { 69 | // Validate 70 | if(box.IsEmpty) 71 | return new Box(); 72 | 73 | // Extract 74 | var extract = box.Extract; 75 | 76 | // Transform and LiftTo 77 | Box liftedResult = bind(extract); 78 | 79 | if(liftedResult.IsEmpty) 80 | return new Box(); 81 | 82 | // Project/Combine 83 | TC t2 = project(extract, liftedResult.Extract); 84 | return new Box(t2); 85 | } 86 | 87 | /// 88 | /// Validate, Extract, Transform and Lift (If Valid) 89 | /// Check/Validate then transform to T and lift into Box 90 | /// 91 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 92 | { 93 | // Validate 94 | if(box.IsEmpty) 95 | return new Box(); 96 | 97 | //Extract 98 | TA extract = box.Extract; 99 | 100 | // Transform and lift 101 | Box transformedAndLifted = bind(extract); 102 | 103 | return transformedAndLifted; 104 | } 105 | 106 | /// 107 | /// Validate, Extract, Transform and automatic Lift (If Valid) 108 | /// 109 | public static Box Map(this Box box, Func select /*Transform*/) 110 | { 111 | // Validate 112 | if(box.IsEmpty) 113 | return new Box(); 114 | 115 | // Extract 116 | TA extract = box.Extract; 117 | 118 | // Transform 119 | TB transformed = select(extract); 120 | 121 | // Lift 122 | return new Box(transformed); 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Tutorial12_function_compositionial12/Tutorial12 - function composition.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial12\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial13_pure_functions/Box.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Reflection.PortableExecutable; 5 | using System.Runtime.InteropServices.ComTypes; 6 | using System.Threading.Tasks.Dataflow; 7 | 8 | namespace Tutorial13 9 | { 10 | /// 11 | /// A box can hold 1 thing only 12 | /// 13 | /// 14 | public class Box 15 | { 16 | public Box(T newExtract) 17 | { 18 | Extract = newExtract; 19 | IsEmpty = false; 20 | } 21 | 22 | public Box() { } 23 | 24 | private T _extract; 25 | 26 | public T Extract 27 | { 28 | get => _extract; 29 | set 30 | { 31 | _extract = value; 32 | IsEmpty = false; 33 | } 34 | } 35 | 36 | public bool IsEmpty = true; 37 | public override string ToString() 38 | => !IsEmpty ? $"{_extract}" : "The box is empy"; 39 | 40 | } 41 | 42 | public static class BoxMethods 43 | { 44 | /// 45 | /// Validate, Extract, Transform and Lift (If Valid) 46 | /// 47 | public static Box Select(this Box box, Func map) 48 | { 49 | // Validate 50 | if (box.IsEmpty) 51 | return new Box(); 52 | 53 | // Extract 54 | var extracted = box.Extract; 55 | 56 | // Transform 57 | TB transformedItem = map(extracted); 58 | 59 | // Lift 60 | return new Box(transformedItem); 61 | } 62 | 63 | /// 64 | /// Validate, Extract, Transform, Project(Transform, Extract) and automatic Lift 65 | /// 66 | 67 | public static Box SelectMany(this Box box, Func> bind /*liftTo*/, Func project) 68 | { 69 | // Validate 70 | if(box.IsEmpty) 71 | return new Box(); 72 | 73 | // Extract 74 | var extract = box.Extract; 75 | 76 | // Transform and LiftTo 77 | Box liftedResult = bind(extract); 78 | 79 | if(liftedResult.IsEmpty) 80 | return new Box(); 81 | 82 | // Project/Combine 83 | TC t2 = project(extract, liftedResult.Extract); 84 | return new Box(t2); 85 | } 86 | 87 | /// 88 | /// Validate, Extract, Transform and Lift (If Valid) 89 | /// Check/Validate then transform to T and lift into Box 90 | /// 91 | public static Box Bind(this Box box, Func> bind /*liftAndTransform*/) 92 | { 93 | // Validate 94 | if(box.IsEmpty) 95 | return new Box(); 96 | 97 | //Extract 98 | TA extract = box.Extract; 99 | 100 | // Transform and lift 101 | Box transformedAndLifted = bind(extract); 102 | 103 | return transformedAndLifted; 104 | } 105 | 106 | /// 107 | /// Validate, Extract, Transform and automatic Lift (If Valid) 108 | /// 109 | public static Box Map(this Box box, Func select /*Transform*/) 110 | { 111 | // Validate 112 | if(box.IsEmpty) 113 | return new Box(); 114 | 115 | // Extract 116 | TA extract = box.Extract; 117 | 118 | // Transform 119 | TB transformed = select(extract); 120 | 121 | // Lift 122 | return new Box(transformed); 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Tutorial13_pure_functions/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | namespace Tutorial13 5 | { 6 | // This tutorial shows you what a pure function is. 7 | // A pure function's return value is a product of its arguments ie only its arguments are used to determine the return value. 8 | // The expectation is that if it does this, then the same input to the function will yield the same output too. 9 | // To ensure this, you need to further restrict the function to not use/depend on anything that might jeopordise this, ie fetch/use a source that today might be one thing and tomorrow might be something else 10 | // for instance, if you get a value from the DB today, tomorrow it might be removed and then the guarantee you made that the function will return the same value breaks. 11 | // So you can't use Input/Output as that is the source of changeable circumstances. 12 | 13 | // Side note: Pure functions are immediately parallelizable and can be used for Memoization (storing the result and input of the function requires that the function never needs to run again) 14 | class Program 15 | { 16 | static void Main(string[] args) 17 | { 18 | 19 | Box aBoxOfNumbers = new Box(new int[]{ 1, 2, 3, 4, 5, 6, 7}); 20 | 21 | var result = aBoxOfNumbers.Map(numbers => GenerateFibonacciSeriesFrom(numbers)); // note non-method group notation(not important) 22 | var result2 = aBoxOfNumbers.Map(ImpureGenerateFibonacciSeries); // not method group notation(not important) 23 | 24 | Console.WriteLine($"fibos are {string.Join(',', result.Extract)} and are gaurenteed to be this provided the same input is used"); 25 | Console.WriteLine($"fibos generated from impure function are {string.Join(',', result2.Extract)} is not to be this always."); 26 | 27 | // Pure function only uses its input to generate its result and doesn't depend/get/fetch data from anywhere else that might impact the result. 28 | // It certainly does not depend on things like I/O and db calls that might not bring the same result on subsequent calls of the function 29 | // and it does not throw exceptions(later tutorial show how to remove exceptions from your code using Either). 30 | int[] GenerateFibonacciSeriesFrom(int[] numbers) 31 | { 32 | var fibs = new List(numbers.Length); 33 | for (var i = 0; i < numbers.Length + 1; i++) 34 | { 35 | if (i > 1) 36 | fibs.Insert(i, i - 1 + i); 37 | else 38 | fibs.Insert(i, i); 39 | } 40 | 41 | return fibs.ToArray(); 42 | } 43 | 44 | int[] ImpureGenerateFibonacciSeries(int[] numbers) 45 | { 46 | var fibs = new List(numbers.Length); 47 | for (var i = 0; i < numbers.Length + 1; i++) 48 | { 49 | fibs.Insert(i, i > 1 ? AddFn(i - 1, i) : i); 50 | } 51 | 52 | // Impure add function because it is not guarantee to always do a number1+number2 addition consistently, as it depends on the day of the week which can change the result 53 | // even though the input numbers are the same on calls to it. 54 | int AddFn(int number1, int number2) 55 | { 56 | if(DateTime.Today.DayOfWeek == DayOfWeek.Thursday) // Dependency on this condition breaks guarantee that the same input provided in number1 and number2 will ALWAYS yield the same result. 57 | return number1 + number2; 58 | else 59 | return (number1 + number2 + 1); 60 | } 61 | 62 | // Eg. Input is 1,2,3 63 | // On Monday: 0,1,4,6,8,110,12,14 64 | // On Tuesday: 0,1,4,6,8,110,12,14 65 | // On Wednesday: 0,1,4,6,8,110,12,14 66 | // On Thursday: 0,1,3,5,7,9,11,13 <--- Breaks that promise that for input 1,2,3 you get the same out put as 0,1,4,6,8,110,12,14 67 | // On Friday: 0,1,4,6,8,110,12,14 68 | // On Saturday: 0,1,4,6,8,110,12,14 69 | // On Sunday: 0,1,4,6,8,110,12,14 70 | 71 | return fibs.ToArray(); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tutorial13_pure_functions/Tutorial13 - pure functions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial13\ 7 | 2.0 8 | 9 | -------------------------------------------------------------------------------- /Tutorial14_Intro_to_Eithers/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial14 8 | { 9 | // This tutorial shows you what a what an Either<> type is and how to use it generally 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | string word = "five"; 16 | 17 | // Note: We're creating an either with the left type as int and the right type as string. 18 | // You can assign either type to an either 19 | Either amount = 5; 20 | amount = word; 21 | var emptyEither = new Either(); 22 | 23 | // Note you can do the same transformations you did on a Box on an Either because it too is a Monad and it too has a Bind(), Map(), Select() and SelectMany() extension method. 24 | // This transformation occurs on the right hand value, provided that the either contains the right hand value and not the left (this is the inbuilt validation or short-circuiting Monad idea in action) 25 | 26 | var resultA = amount.Bind(str => TransformRight(str)); // Extract EitherData from either once validated ie that its not a right value, run transform function. 27 | 28 | amount = 25; 29 | 30 | // notice that it's biased to its right value and only cares about running a transform on a right type. 31 | // The validation within Either's Bind() will check for a Right Type and then run the provided transform, if its left it will return what it has (25) but no transform will occur 32 | var resultB = amount.Bind(integer => TransformRight(integer)); // Wont run transformation because the validation will fail because its a left type. 33 | 34 | Console.WriteLine($"The value of resulta is '{resultA}' and the result of result b is '{resultB}' and an empty either looks like this '{emptyEither}' "); 35 | 36 | // notice how Either's bind function will extract the right part, if validation succeeds and runs the transformation as expected. 37 | Either TransformRight(string extractedRight) 38 | { 39 | Either result = extractedRight.ToUpper(); // Like all Bind functions we need to lift the result into the monad ie Either type 40 | return result; 41 | } 42 | 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tutorial14_Intro_to_Eithers/Tutorial14 - Intro to Eithers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial14\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial15_BiBind/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial15 8 | { 9 | // This tutorial shows you what a what an Either<>'s BiBind() functionality 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | 16 | int number = 5; 17 | string word = "five"; 18 | 19 | Either amount = 5; 20 | amount = word; 21 | 22 | // Instead of only being able to run a transform on the right hand side only as bind() does, you can use BiBind() to prepare transform functions for whatever side, left or right type is assigned to it! 23 | // The result is the result of whichever bind run, depending which state the either is in (left=has integer)(right=has string) 24 | var resultOfTransform = amount.BiBind(rightString => TransformExtractedRight(rightString), leftInteger => TransformExtractedLeft(leftInteger)); 25 | 26 | Console.WriteLine($"the result of the transform is {resultOfTransform}"); 27 | 28 | amount = number; 29 | 30 | resultOfTransform = amount.BiBind(rightString => TransformExtractedRight(rightString), leftInteger => TransformExtractedLeft(leftInteger)); 31 | Console.WriteLine($"the result of the transform is {resultOfTransform}"); 32 | 33 | 34 | Either TransformExtractedRight(string rightString) 35 | { 36 | Either ret = rightString + " is a word, i think"; 37 | return ret; 38 | } 39 | 40 | Either TransformExtractedLeft(int leftInteger) 41 | { 42 | Either ret = leftInteger + 1; 43 | return ret; 44 | } 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tutorial15_BiBind/Tutorial15 - BiBind().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial15\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial16_BiExists/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial16 8 | { 9 | // This tutorial shows you what a what an Either<>'s BiExists() 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | 16 | int number = 5; 17 | string word = "five"; 18 | 19 | Either amount = 5; 20 | amount = word; 21 | 22 | // like BiBind() allows you to provide both transform functions and the correct transform will run depending on is it is a right or left type contained within it - you can do something similar here. 23 | // BiExists allows you to test/use/inspect the content and return true/false based on it. 24 | // BiExists can be viewed as a the 'existance' of a provided validation check being successful, specific to each type left or right 25 | bool isEitherGreaterThanNothing = amount.BiExists(stringRight => stringRight.Length > 0 ? true: false, integerleft => integerleft > 0 ? true : false); 26 | 27 | Console.WriteLine($"The result value is {isEitherGreaterThanNothing}"); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tutorial16_BiExists/Tutorial16 - BiExists().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial15\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial17_Fold/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial17 8 | { 9 | // This tutorial shows you what fold() does 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Either intOrString = "start"; 16 | 17 | // A state (InitialResult) changes over time and it changes using results of the previous change.... It uses an new item extracted from the array in changing the state each time. 18 | // The state changes the number of elements in the either - there will only be one - Left or right type. 19 | // the state will change once based on the one value in the either. 20 | // For a List which has multiple items in it, the state will change that many times 21 | var result = intOrString.Fold("InitialState", (previousResult, extract) => changeState(extract, previousResult)); 22 | 23 | // The result is the last state change 24 | Console.WriteLine($"The result value is {result}"); 25 | 26 | string changeState(EitherData extracted, string previousResult) 27 | { 28 | var content = extracted.State == EitherStatus.IsLeft ? $"{extracted.Left}" : $"{extracted.Right}" ; 29 | var newResult = $"{previousResult} and {content}"; 30 | return newResult; 31 | } 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tutorial17_Fold/Tutorial17 - Fold().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial15\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial18_Iter/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial18 8 | { 9 | // Iter: run an arbitary function on the Either<> if its value is right type or chose BiIter() to specify a function to run on both types 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Either intOrString = 45; // put it in left state by default (integer) 16 | 17 | bool didFunctionRunForRight = false; 18 | bool didFunctionRunForLeft = false; 19 | 20 | // extracts the right content and if it is right, it run this non-transforming ie void returning function on it. The function wont run it its as left type 21 | intOrString.Iter(rightString => RunAFunctionOnRightContents(rightString)); // So only runs the function if its right type 22 | 23 | // both actions are void returning (Unit represents a typed void result) 24 | Unit ret = intOrString.BiIter(rightString => RunAFunctionOnRightContents(rightString), leftInteger => didFunctionRunForLeft = true); 25 | 26 | Console.WriteLine($"Did function run on right contents? {didFunctionRunForRight}"); 27 | Console.WriteLine($"Did function run on left contents? {didFunctionRunForLeft}"); 28 | 29 | void RunAFunctionOnRightContents(string str) 30 | { 31 | Console.WriteLine($"Hello {str}"); 32 | didFunctionRunForRight = true; 33 | 34 | } 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tutorial18_Iter/Tutorial18 - Iter().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial18\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial19_BiMap/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial19 8 | { 9 | // Using BiMap() to make provision for a transform function for both the left and right types of the either. The transform is automatically lifted. 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Either intOrString = 45; 16 | 17 | // This will extract the item and allow you to run a transformation like a Map, which automatically lifts the result of the transform. it will allow you to specify this 18 | // transform function for both types that the either can hold and the correct one will run depending on the actual type that the either holds at that moment in time. 19 | var result = intOrString.BiMap(rightString => rightString.ToUpper(), leftInteger => leftInteger + 1024); 20 | 21 | Console.WriteLine($"The result is {result}"); 22 | 23 | intOrString = "Stuart"; 24 | result = intOrString.BiMap(rightString => rightString.ToUpper(), leftInteger => leftInteger + 1024); 25 | 26 | 27 | Console.WriteLine($"The result is {result}"); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tutorial19_BiMap/Tutorial19 - BiMap().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial19\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial20_BindLeft/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial20 8 | { 9 | // Shows the basics of Either, using BindLeft() to make provision for a transform function for the left types of the either (which is unusual for the default Bind() function). 10 | // The transform is NOT automatically lifted(this is a bind() after all). 11 | 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | Either intOrString = "Stuart"; 17 | 18 | // Transform the left hand side, remember bind will not automatically life the result of that transformation, so you transformation function will need to do that 19 | // We call BindLeft because by default Either is right biased so default Bind() only transforms the right side if there is one(there there isn't as we've assigned a right value of string 'Stuart' 20 | var result = intOrString.BindLeft(left => TransformLeft(left)); 21 | 22 | // Note the transformation did not occur because either contained a right type ie string 23 | Console.WriteLine($"result is {result}"); 24 | 25 | intOrString = 55; 26 | // Note the transformation should now occur because either contained a left type and we've defined a transformation for that on this either 27 | result = intOrString.BindLeft(left => TransformLeft(left)); 28 | 29 | Console.WriteLine($"result is {result}"); 30 | 31 | Either TransformLeft(int left) 32 | { 33 | Either transformedResult = left + 22; 34 | return transformedResult; 35 | } 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tutorial20_BindLeft/Tutorial20 - BindLeft().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial20\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial21_Match/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial20 8 | { 9 | // Using Match to extract the contents of an Either<> but not put it back into and either types (as map() and Bind() would do) 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Either intOrString = "Stuart"; 16 | 17 | // Match will run a function for which ever type is contained within the either. 18 | // Transform functions for both types of content are specified. The function will run depending on the underlying type in the either. 19 | // The return result of each function must be the same type (both string or both int) such that you can assign the result of the Match to a types variable 20 | // Only one function will run, as only one of the two types can be in the either at any one moment in time. 21 | string result = intOrString.Match(rightString => $"Right value is {rightString}", leftInteger => $"left value is {leftInteger}"); 22 | 23 | Console.WriteLine($"Result is {result}"); 24 | 25 | intOrString = 32; 26 | result = intOrString.Match(rightString => $"Right value is {rightString}", leftInteger => $"left value is {leftInteger}"); 27 | 28 | Console.WriteLine($"Result is {result}"); 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tutorial21_Match/Tutorial21 Match().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial21\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial22_BiMapT_and_MapT/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial22 8 | { 9 | // This tutorial shows you how you can transform a List of Eithers, effectively doing a Map on each either in the list, and this Bi variety allows you to specify how make provision to map/transform both types 10 | // When a Bind/Map function is called on a list of monads, it is BindT or BiMapT, otherwise operating on a single monad, use Bind() or Map() alone 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Either intOrString1 = "Stuart"; 16 | Either intOrString2 = "Jenny"; 17 | Either intOrString3 = "Bruce"; 18 | Either intOrString4 = "Bruce"; 19 | Either intOrString5 = 66; 20 | 21 | IEnumerable> listOfEithers = new Either[] { intOrString1, intOrString2, intOrString3, intOrString4, intOrString5 }; 22 | 23 | // Extracts the rights only, ignores the lefts(they disapear from the result) - not lifted back into either - comparable to a Match() in this way 24 | IEnumerable rights = listOfEithers.Rights(); 25 | IEnumerable lefts = listOfEithers.Lefts(); 26 | 27 | // Runs a map transform function(automatic lift) on every either in the lift 28 | // Remember a map and a Bind can change the contained type of the returned either but it must be an either 29 | // the T in BiMapT really inndicates that the BiMapT works on a list of monads, in this case a list of Eithers 30 | IEnumerable> result = listOfEithers.BiMapT(rightString => $"TurnAllRightStringToThis ({rightString})", leftInteger => 'A' /*Turn all left values in the eithers to 'A'*/).ToList(); 31 | 32 | // ok so now we've transformed the Eithers in different ways depending on their type, lets look at them 33 | 34 | // we could look at our transformations done on each using this: 35 | var lefts1 = result.Lefts(); 36 | var rights1 = result.Rights(); 37 | 38 | // or we could get a string representation of either type of the either and print that to show us how that either was transformed 39 | foreach (var either in result) 40 | { 41 | var stringResult = either.Match(rightString => rightString, leftInteger => $"{leftInteger}"); 42 | Console.WriteLine(stringResult); 43 | } 44 | 45 | // Transform each either that has in in the right state, ie has a value of the right type 46 | // Remember to bind is to lift by yourself 47 | var result1 = result.MapT(rightString => TransformMe(rightString)); 48 | 49 | // if you prefer to transform the left type for each either in the list, you can: 50 | var result2 = result.MapLeftT(c => char.ToUpper(c)); 51 | 52 | // You can also transform the eithers in the list using a bind(not automatic lift) 53 | 54 | var result3 = result.BindT(s => 55 | { 56 | Either either = 'c'; // we need to lift a bind transformation function as always 57 | return either; 58 | }); 59 | 60 | 61 | 62 | Either TransformMe(string rightString) 63 | { 64 | return $"the value is {rightString}"; 65 | 66 | } 67 | 68 | Console.WriteLine($"mapT result is:"); 69 | foreach (var either in result1) 70 | { 71 | var str = either.Match(Right: (rightString) => $"String is '{rightString}'", Left: (leftChar) => $"Char is '{leftChar}'"); 72 | Console.WriteLine(str); 73 | } 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tutorial22_BiMapT_and_MapT/Tutorial22 - BiMapT() and MapT().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial22\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial23_BindT/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial23 8 | { 9 | // This tutorial shows you how you can transform a List of Eithers, effectively doing a Bind on each either in the list 10 | // and a Bi variety allows you to specify how make provision to map/transform both types 11 | 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | Either intOrString1 = "Stuart"; 17 | Either intOrString2 = "Jenny"; 18 | Either intOrString3 = "Bruce"; 19 | Either intOrString4 = "Bruce"; 20 | Either intOrString5 = 66; 21 | 22 | IEnumerable> listOfEithers = new Either[] { intOrString1, intOrString2, intOrString3, intOrString4, intOrString5 }; 23 | 24 | // transform the right values (if they are there) for each either in the list 25 | // As this is a bind, you need to lift the result in to a Either 26 | var transformedList = listOfEithers.BindT(rightString => TransformRight(rightString)); 27 | 28 | Either TransformRight(string rightString) 29 | { 30 | Either t = $"My name is '{rightString}'"; 31 | return t; 32 | } 33 | 34 | var newRights = transformedList.Rights(); /* note we dont care care about the lefts, 35 | if we did we might you match to see what both left and right values would be if they are set on the eithers we are looking at - see Tutorial 22 */ 36 | foreach (var str in newRights) 37 | { 38 | Console.WriteLine(str); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tutorial23_BindT/Tutorial23 - BindT().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial23\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial24_IterT/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LanguageExt; 5 | using LanguageExt.DataTypes.Serialisation; 6 | 7 | namespace Tutorial24 8 | { 9 | // This tutorial shows you how you can call a function on each right value for the list of monads in the list, using IterT() (Either is a monad) 10 | 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Either intOrString1 = "Stuart"; 16 | Either intOrString2 = "Jenny"; 17 | Either intOrString3 = "Bruce"; 18 | Either intOrString4 = "Bruce"; 19 | Either intOrString5 = 66; 20 | 21 | IEnumerable> listOfEithers = new Either[] { intOrString1, intOrString2, intOrString3, intOrString4, intOrString5 }; 22 | 23 | // Extract right values from the eithers in the list and run this function to get them. 24 | // note if there is no right value for the either being inspected, this function is not run 25 | // ie skips 66 26 | listOfEithers.IterT(rightString => Console.WriteLine($"{rightString}")); 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tutorial24_IterT/Tutorial24 - IterT().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial23\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial25_Apply/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Security.Cryptography.X509Certificates; 6 | using LanguageExt; 7 | using LanguageExt.DataTypes.Serialisation; 8 | 9 | namespace Tutorial25 10 | { 11 | // Using Apply both on a simple Either<> and a List of Eithers to demonstrates its simplicity 12 | 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | Either intOrString1 = "Stuart"; 18 | Either intOrString2 = "Jenny"; 19 | Either intOrString3 = "Bruce"; 20 | Either intOrString4 = "Bruce"; 21 | Either intOrString5 = 66; 22 | 23 | // basically pass yourself ie your current value to the function provided. Ie apply allows you to transform yourself (makes a copy of the result, not an in-place modification) 24 | var resultA = intOrString5.Apply(me => UseThis(me)); 25 | 26 | // the function can transform the type of the either 27 | var resultB = intOrString5.Apply(me => UseThisAndChangeType(me)); 28 | 29 | Console.WriteLine($"ResultA = {resultA}, ResultB = {resultB}"); 30 | 31 | IEnumerable> listOfEithers = new Either[] { intOrString1, intOrString2, intOrString3, intOrString4, intOrString5 }; 32 | 33 | // So in the same way, give your self ie your content (which is an List of Eithers) to the provided function 34 | var result = listOfEithers.Apply(enumerable => UseThisListOfEithers(enumerable)); 35 | 36 | Console.WriteLine($"The result of is '{result}'"); 37 | 38 | Either UseThis(Either useThis) 39 | { 40 | var str = useThis.Match(rightString => rightString, leftInteger => "was left"); 41 | Either t = str; 42 | return t; 43 | } 44 | 45 | Either UseThisAndChangeType(Either useThis) 46 | { 47 | var str = useThis.Match(rightString => 'T', leftInteger => 'F'); 48 | Either t = str; 49 | return t; 50 | } 51 | 52 | IEnumerable> UseThisListOfEithers(IEnumerable> useMe) 53 | { 54 | // Transform the things in whatever state (type ie left or right) they are in 55 | return useMe.BiMapT(rightString => rightString, leftInteger => leftInteger * 2); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Tutorial25_Apply/Tutorial25 - Apply().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial23\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial26_Partition/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Security.Cryptography.X509Certificates; 6 | using LanguageExt; 7 | using LanguageExt.DataTypes.Serialisation; 8 | 9 | namespace Tutorial26 10 | { 11 | // Using Partition to easily get both the lefts() and the Rights() in one call - as a tuple of (lefts,rights) 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | Either intOrString1 = "Stuart"; 17 | Either intOrString2 = "Jenny"; 18 | Either intOrString3 = "Bruce"; 19 | Either intOrString4 = "Bruce"; 20 | Either intOrString5 = 66; 21 | 22 | IEnumerable> listOfEithers = new Either[] { intOrString1, intOrString2, intOrString3, intOrString4, intOrString5 }; 23 | 24 | // instead of calling Lefts() and Rights(), you can get them all in one go as a tuple 25 | var (lefts, rights) = listOfEithers.Partition(); 26 | 27 | foreach(var left in lefts) 28 | Console.WriteLine($"Left: {left}"); 29 | 30 | foreach (var right in rights) 31 | Console.WriteLine($"Right: {right}"); 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tutorial26_Partition/Tutorial26 - Partition().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial26\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial27_Match/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Security.Cryptography.X509Certificates; 6 | using LanguageExt; 7 | using LanguageExt.DataTypes.Serialisation; 8 | 9 | namespace Tutorial27 10 | { 11 | // Shows the basics of transforming a list of Eithers using match to understand whats in them (both left and right values) and 12 | // then transform them based on their values into a single type that represents either in one way (a string) 13 | // Along with Map() and Bind() this extracts the value from the Either and provides transformation functions for both Left and Right sides of the Either 14 | class Program 15 | { 16 | static void Main(string[] args) 17 | { 18 | Either intOrString1 = "Stuart"; 19 | Either intOrString2 = "Jenny"; 20 | Either intOrString3 = "Bruce"; 21 | Either intOrString4 = "zxcmbasdjkfkejrfg"; 22 | Either intOrString5 = 66; 23 | Either intOrString6 = 234; 24 | 25 | IEnumerable> listOfEithers = new Either[] { intOrString1, intOrString2, intOrString3, intOrString4, intOrString5, intOrString6, }; 26 | 27 | // Go through each of the Eithers and depending on their types and their content, transform them 28 | // and we can represent values from either type, string or int as one uniform type, in this case we can represent a left and a right as a string and 29 | // thus return the set of all these representation as a list of strings 30 | // Note that in order to assign the result of a match, both sides' transformation function needs to return the same type, particularly the type of the receiving variable 31 | 32 | var result = listOfEithers.Match(Right: MakeGenderAwareString, Left: MakeOneHundredAwareString); 33 | 34 | foreach (var transform in result) 35 | { 36 | Console.WriteLine(transform); 37 | } 38 | } 39 | 40 | private static string MakeOneHundredAwareString(int leftInteger) 41 | { 42 | return leftInteger > 100 ? $"{leftInteger} is greater than 100" : $"{leftInteger} is less tan 100"; 43 | } 44 | 45 | private static string MakeGenderAwareString(string rightString) 46 | { 47 | string[] boysNames = new[] {"Stuart", "Bruce"}; 48 | string[] girlsNames = new[] {"Jenny"}; 49 | 50 | if (boysNames.Contains(rightString)) return $"{rightString} is a Boys name"; 51 | if (girlsNames.Contains(rightString)) return $"{rightString} is a Girls name"; 52 | return $"i dont know if {rightString} not registered with me as a boys name or a girls name"; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tutorial27_Match/Tutorial27 - Match().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial27\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial28_Intro_to_Option/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Security.Cryptography.X509Certificates; 6 | using LanguageExt; 7 | using LanguageExt.DataTypes.Serialisation; 8 | 9 | namespace Tutorial28 10 | { 11 | // Option type effectively removes the need to use NULL in your code. Nulls can produce unexpected behavior and as such have no place in pure functions where unexpected behavior would 12 | // render them otherwise impure 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | // An optional type can hold an integer or a none 18 | Option optionalInteger = 34; // note we can just assign it straight away 19 | optionalInteger = Option.Some(34); // save as above. Shown just for demonstration purposes 20 | optionalInteger = Option.None; 21 | // optionalInteger = null; Options effectively eliminate nulls in your code. 22 | 23 | var resultA = DivideBy(25, 5); 24 | var resultB = DivideBy1(optionalInteger, 5); 25 | 26 | Console.WriteLine($"The result A is '{resultA}' and B is '{resultB}'"); 27 | } 28 | 29 | static int DivideBy(int thisNumber, int dividedByThatNumber) 30 | { 31 | return thisNumber / dividedByThatNumber; 32 | } 33 | 34 | static Option DivideBy1(Option thisNumber, int dividedByThatNumber) 35 | { 36 | return thisNumber.Map( i => i / dividedByThatNumber); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tutorial28_Intro_to_Option/Tutorial28 - Intro to Option.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial28\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial29_Option_Basics/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Security.Cryptography.X509Certificates; 6 | using LanguageExt; 7 | using LanguageExt.DataTypes.Serialisation; 8 | 9 | namespace Tutorial29 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | int resultA = DivideBy(25, 5); 16 | Option resultB = DivideBy1(25, 0); 17 | 18 | Console.WriteLine($"The result A is '{resultA}' and B is '{resultB}'"); 19 | 20 | /* 21 | * Discussion: 22 | * DivideBy1 will return an Option and it knows what an invalid result is - its encapsulated within the option 23 | * DivideBy will return an int, but the caller needs to know that 0 is an invalid result. 24 | */ 25 | 26 | var result1 = Add5ToIt(resultA); 27 | var result2 = Add5ToIt(resultB); 28 | 29 | Console.WriteLine($"The result A is '{result1}' and B is '{result2}'"); 30 | } 31 | 32 | 33 | /// 34 | /// Normal function, not using optional parameters 35 | /// 36 | /// 37 | /// 38 | /// integer 39 | static int DivideBy(int thisNumber, int dividedByThatNumber) 40 | { 41 | if (dividedByThatNumber == 0) 42 | return 0; 43 | return thisNumber / dividedByThatNumber; 44 | } 45 | 46 | /// 47 | /// Function returns Monad, one construct that represents both failure and success. 48 | /// 49 | /// Option of an integer 50 | static Option DivideBy1(int thisNumber, int dividedByThatNumber) 51 | { 52 | if (dividedByThatNumber == 0) 53 | { 54 | Option t = Option.None; 55 | return t; 56 | } 57 | 58 | return thisNumber / dividedByThatNumber; 59 | } 60 | 61 | static int Add5ToIt(int input) 62 | { 63 | // Whoops, I've forgotten to check if input is valid. 64 | return input + 5; 65 | // And even if i did, I've have to know what an invalid input is - its 0 in this case. 66 | } 67 | 68 | static Option Add5ToIt(Option input) 69 | { 70 | // I can assume that its valid, because Map will run a transformation function on the valid input 71 | Option t = input.Map(validInput => validInput + 5); // if its invalid the Validation phase of the map() function will return a None ie an invalid input and so the 72 | 73 | // the result of the Map will be an option of None or invalid input in it. 74 | // Remember a Map always returns a Monad, in this case Option and it automatically lifts it for you by Map() 75 | return t; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tutorial29_Option_Basics/Tutorial29 - Option Basics.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial29\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial30_Option_in_functions/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Security.Cryptography.X509Certificates; 6 | using LanguageExt; 7 | using LanguageExt.DataTypes.Serialisation; 8 | 9 | namespace Tutorial30 10 | { 11 | // Contrived example of passing around Option arguments 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | 17 | var resultB = DivideBy1(225, 5); 18 | var result2 = Add5ToIt(resultB); 19 | 20 | // Now we can continue with the knowledge that result2 as 5 added to it or not (and everyone else will do that too:) 21 | 22 | var result = PerformPensionCalculations(result2); 23 | 24 | if (result.IsNone) 25 | theBadMessage(); 26 | 27 | if (result.IsSome) 28 | theGoodMessage(); 29 | 30 | // of we could use a BiIter without any conditionals above (if statements) 31 | 32 | result.BiIter(i => theGoodMessage(), ()=> theBadMessage()); 33 | 34 | void theBadMessage() => Console.WriteLine($"Could not determine your pension, because invalid input was used"); 35 | 36 | void theGoodMessage() 37 | { 38 | var pension = result.Match(Some: i => i, None: 0); 39 | Console.WriteLine($"Your pension is '{pension}'"); 40 | } 41 | 42 | } 43 | 44 | /// 45 | /// Example of passing in a option monad 46 | /// 47 | static Option PerformPensionCalculations(Option input) 48 | { 49 | // Extract, Validate and transform it using Map (reember is a Monad) 50 | return input.Map(validInput => CalculateYourPension(validInput)); 51 | // we can call a normal function (non monad returning or accepting) inside a Map or Bind 52 | } 53 | 54 | 55 | static int CalculateYourPension(int input) 56 | { 57 | return (input * 3) / 26; 58 | } 59 | 60 | static Option DivideBy1(int thisNumber, int dividedByThatNumber) 61 | { 62 | if (dividedByThatNumber == 0) 63 | { 64 | Option t = Option.None; 65 | return t; 66 | } 67 | 68 | return thisNumber / dividedByThatNumber; 69 | } 70 | 71 | static Option Add5ToIt(Option input) 72 | { 73 | // View a Bind/Map as 'Try to add 5' if the input is valid ie not a None 74 | return input.Bind(validInput => 75 | { 76 | Option option = validInput + 5; 77 | return option; 78 | }); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Tutorial30_Option_in_functions/Tutorial30 - Option in functions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial30\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial31_IfSome_and_IfNone/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Security.Cryptography.X509Certificates; 6 | using LanguageExt; 7 | using LanguageExt.DataTypes.Serialisation; 8 | 9 | // Demonstrates the usage of IfNone and IfSome which runs a user defined function provided the option is None or Some respectively 10 | namespace Tutorial31 11 | { 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | 17 | var resultB = DivideBy1(225, 5); 18 | 19 | var result2 = Add5ToIt(resultB); 20 | 21 | // Now we can continue with the knowledge that result2 as 5 added to it or not (and everyone else can do that too...) 22 | 23 | var result = PerformPensionCalculations(result2); 24 | 25 | Unit noValue = result.IfSome(validInput => Console.WriteLine("yay valid input is {validInput}")); 26 | int defaultPensionIfInvalidInput = result.IfNone(() => -1); // IfNone turns your result into a valid value, effectively a Some() but its actual value, int 27 | } 28 | 29 | static Option PerformPensionCalculations(Option input) 30 | { 31 | // Extract, Validate and transform it using Map 32 | return input.Map(validInput => CalculateYourPension(validInput)); 33 | // We can call a normal function (non monad returning or accepting) inside a Map or Bind 34 | } 35 | 36 | 37 | static int CalculateYourPension(int input) 38 | { 39 | return (input * 3) / 26; 40 | } 41 | 42 | static Option DivideBy1(int thisNumber, int dividedByThatNumber) 43 | { 44 | if (dividedByThatNumber == 0) 45 | { 46 | Option t = Option.None; 47 | return t; 48 | } 49 | 50 | return thisNumber / dividedByThatNumber; 51 | } 52 | 53 | static Option Add5ToIt(Option input) 54 | { 55 | // View a Bind/Map as 'Try to add 5' if the input is valid ie not a None 56 | return input.Bind(validInput => 57 | { 58 | Option option = validInput + 5; 59 | return option; 60 | }); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tutorial31_IfSome_and_IfNone/Tutorial31 - IfSome() and IfNone().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial31\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial32_Pipelining_with_Optionsial32/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using LanguageExt; 8 | using LanguageExt.DataTypes.Serialisation; 9 | 10 | namespace Tutorial32 11 | { 12 | // Rosetta code! Procedural -> Fluent -> Query Syntax 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | int startingAmount = 225; 18 | 19 | // Procedural way 20 | var step1 = DivideBy(startingAmount, 5); 21 | var step2 = Add5ToIt(step1); 22 | var result = PerformPensionCalculations(step2); 23 | 24 | // Fluent way 25 | Option result1 = DivideBy1(startingAmount, 5) 26 | .Bind(input => Add5ToIt1(input)) 27 | .Bind(input => PerformPensionCalculations1(input)); 28 | 29 | // Expression way 30 | Option result2 = from input1 in DivideBy1(startingAmount, 5) 31 | from input2 in Add5ToIt1(input1) 32 | from input3 in PerformPensionCalculations1(input2) 33 | select input3; 34 | 35 | // Oh Wow, we've really simplified the code, its smaller, all the steps are functions and we are happy 36 | // Passing and sending Monads have helped us create step-by-step logical means to execute code entirely from functions! 37 | } 38 | 39 | static Option PerformPensionCalculations1(Option input) 40 | => input.Map(CalculateYourPension); 41 | 42 | static int PerformPensionCalculations(int input) 43 | => CalculateYourPension(input); 44 | 45 | static int CalculateYourPension(int input) 46 | => (input * 3) / 26; 47 | 48 | static int DivideBy(int thisNumber, int dividedByThatNumber) 49 | => dividedByThatNumber == 0 ? 0 : thisNumber / dividedByThatNumber; 50 | 51 | static Option DivideBy1(int thisNumber, int dividedByThatNumber) => 52 | dividedByThatNumber != 0 53 | ? thisNumber / dividedByThatNumber 54 | : Option.None; 55 | 56 | static Option Add5ToIt1(Option input) 57 | => input.Map(validInput => validInput + 5); 58 | 59 | static int Add5ToIt(int input) 60 | => input + 5; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tutorial32_Pipelining_with_Optionsial32/Tutorial32 - Pipelining with Options.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial32\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial33_ToEither/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using LanguageExt; 8 | using LanguageExt.DataTypes.Serialisation; 9 | 10 | namespace Tutorial33 11 | { 12 | // ToEither extension method to convert a value to a right sided Either 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | //Procedural way 18 | int startingAmount = 225; 19 | var step1 = DivideBy(startingAmount, 5); 20 | var step2 = Add5ToIt(step1); 21 | var result = PerformPensionCalculations(step2); 22 | 23 | 24 | // Fluent way 25 | Option result1 = DivideBy1(startingAmount, 5) 26 | .Bind(input => Add5ToIt1(input)) 27 | .Bind(input => PerformPensionCalculations1(input)); 28 | 29 | // The some value of the Option ie the integer will be the right value of the either, adn then you need to choose a left value: 30 | Either either = result1.ToEither(() => 23); 31 | 32 | } 33 | 34 | static Option PerformPensionCalculations1(Option input) 35 | => input.Map(CalculateYourPension); 36 | 37 | static int PerformPensionCalculations(int input) 38 | => CalculateYourPension(input); 39 | 40 | 41 | static int CalculateYourPension(int input) 42 | => (input * 3) / 26; 43 | 44 | static int DivideBy(int thisNumber, int dividedByThatNumber) 45 | => dividedByThatNumber == 0 ? 0 : thisNumber / dividedByThatNumber; 46 | 47 | static Option DivideBy1(int thisNumber, int dividedByThatNumber) => 48 | dividedByThatNumber != 0 49 | ? thisNumber / dividedByThatNumber 50 | : Option.None; 51 | 52 | static Option Add5ToIt1(Option input) 53 | => input.Map(validInput => validInput + 5); 54 | 55 | static int Add5ToIt(int input) 56 | => input + 5; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tutorial33_ToEither/Tutorial33 - ToEither.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial33\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial34_BiMap()/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using LanguageExt; 8 | using LanguageExt.DataTypes.Serialisation; 9 | 10 | namespace Tutorial34 11 | { 12 | // BiMap() 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | //Procedural way 18 | 19 | var step1 = DivideBy(225, 5); 20 | var step2 = Add5ToIt(step1); 21 | var result = PerformPensionCalculations(step2); 22 | 23 | 24 | // Fluent way 25 | Option result1 = DivideBy1(225, 5) 26 | .Bind(input => Add5ToIt1(input)) 27 | .Bind(input => PerformPensionCalculations1(input)); 28 | 29 | // Transform both the invalid and valid versions of the options to a single string 30 | Option results = result1.BiMap(Some: validInteger => "Valid", None: () => "Invalid"); 31 | 32 | Console.WriteLine($"The result is '{results}'"); 33 | 34 | } 35 | 36 | static Option PerformPensionCalculations1(Option input) 37 | => input.Map(CalculateYourPension); 38 | 39 | static int PerformPensionCalculations(int input) 40 | => CalculateYourPension(input); 41 | 42 | 43 | static int CalculateYourPension(int input) 44 | => (input * 3) / 26; 45 | 46 | static int DivideBy(int thisNumber, int dividedByThatNumber) 47 | => dividedByThatNumber == 0 ? 0 : thisNumber / dividedByThatNumber; 48 | 49 | static Option DivideBy1(int thisNumber, int dividedByThatNumber) => 50 | dividedByThatNumber != 0 51 | ? thisNumber / dividedByThatNumber 52 | : Option.None; 53 | 54 | static Option Add5ToIt1(Option input) 55 | => input.Map(validInput => validInput + 5); 56 | 57 | static int Add5ToIt(int input) 58 | => input + 5; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tutorial34_BiMap()/Tutorial34 - BiMap().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial34\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial35_ThrowIfFailed()/CustomExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using LanguageExt; 6 | 7 | namespace Tutorial35 8 | { 9 | // Note a 'Successful' either is in the Right state while a 'unsucessful' either is in Left state 10 | public static class EitherExtensions 11 | { 12 | /// 13 | /// Throws an exception if the either is in left state ie failed 14 | /// 15 | public static TRight ThrowIfFailed(this Either either) where TLeft : IAmFailure 16 | => either.IfLeft(failure => throw new UnexpectedFailureException(failure)); 17 | 18 | //Given Either in Failure state (IAmFailure) to None, if the check on the failure fails - ie checking if IAmFailure is suitable 19 | public static Either> FailureToNone(this Either> item, Func predicate) 20 | => item.Match(rightValue => rightValue, failure => !predicate(failure) 21 | ? Prelude.Left>(failure) 22 | : Option.None, null); 23 | 24 | // Given Either with an optional Right state. If right state is none, make a failure using provided failure, otherwise use the valid value as the valid/right value of the Either 25 | public static Either NoneToFailure(this Either> item, Func failure) 26 | => item.Bind(e => e.Match(value => value.ToSuccess(), () => Prelude.Left(failure()))); 27 | 28 | /// 29 | /// Given An Either with an optional Right value. If option value is valid/some, make a Either Failure using provided failure, otherwise a Successful Either 30 | /// 31 | public static Either> SomeToFailure(this Either> item, Func failure) 32 | => item.Bind(either => either.Match(_ => Prelude.Left>(failure()), () => either.ToSuccess())); 33 | 34 | public static Func>, Either>> NoneToFailure(Func failure) 35 | => items => items.Sequence().Match(item => item.ToSuccess>(), () => Prelude.Left>(failure())); 36 | 37 | /// 38 | /// Make a Either in left(failure) state if the list is empty (use failure producing function provided), otherwise return the either in right state using the first item in the list as the light value in the either 39 | /// 40 | public static Either HeadOrFailure(this IEnumerable list, Func failure) 41 | => list.Match(() => Prelude.Left(failure()), s => Prelude.Right(s.First())); 42 | 43 | /// 44 | /// Make Either in right state 45 | /// 46 | public static Either ToSuccess(this T value) 47 | => Prelude.Right(value); 48 | 49 | // Make an IAmFailure to a Either ie convert a failure to a either that represents that failure ie an either in the left state 50 | public static Either ToEither(this IAmFailure failure) 51 | => Prelude.Left(failure); 52 | 53 | } 54 | 55 | public class UnexpectedFailureException : Exception 56 | { 57 | public UnexpectedFailureException(IAmFailure failure) 58 | { 59 | 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tutorial35_ThrowIfFailed()/IAmFailure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Tutorial35 6 | { 7 | public interface IAmFailure 8 | { 9 | string Reason { get; set; } 10 | } 11 | 12 | public class GenericFailure : IAmFailure 13 | { 14 | public GenericFailure(string reason) 15 | { 16 | Reason = reason; 17 | } 18 | 19 | public string Reason { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tutorial35_ThrowIfFailed()/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using LanguageExt; 8 | using LanguageExt.DataTypes.Serialisation; 9 | using Tutorial35; 10 | 11 | namespace Tutorial35 12 | { 13 | // ThrowIfFailed and a way to make functions return a standard return type of an Either of Right(T) or a failure(Left). 14 | // T can be any type your function deals with, as you'd use in any normal function you create, only you make it an Option. 15 | // You also bundle with your return type a failure if there is one, by returning an all encompassing return value of Either> 16 | 17 | class Program 18 | { 19 | static void Main(string[] args) 20 | { 21 | // Note that a common Either of form Either> is used here to 22 | // 1) Communicate if there anything or any reason while processing the Option that is erroneous - that will then fail-fast and return a IAmFailure ie Either in Left state 23 | // 2) If there wasn't while inspecting the Option return that or a transformation of that ie an Option 24 | var result1 = DivideBy(225, 5) 25 | .Bind(input => Add5ToIt(input).ThrowIfFailed()) // throw if failed either, otherwise extract and return the right value 26 | .Bind(input => PerformPensionCalculations(input).ThrowIfFailed()); // Inspect for either for a failure and throw if it is, otherwise return the value as-is 27 | 28 | Console.WriteLine($"The result is '{result1}'"); 29 | } 30 | 31 | static Either> PerformPensionCalculations(Option input) 32 | { 33 | /* Remember, We can return a IAmFailure or a Option */ 34 | 35 | // Generate a failure while looking at the option, by extracting the value and determining if its valid or not 36 | var isGreaterThan100 = input.Match(Some: number => number > 100 ? true : false, None: false); 37 | if (!isGreaterThan100) 38 | return new GenericFailure("Must be greater than 100"); 39 | // Do some validation and fail fast, otherwise return the value 40 | return input.Map(CalculateYourPension); //return Option 41 | } 42 | 43 | static int CalculateYourPension(int input) 44 | => (input * 3) / 26; 45 | 46 | static Option DivideBy(int thisNumber, int dividedByThatNumber) => 47 | dividedByThatNumber != 0 48 | ? thisNumber / dividedByThatNumber 49 | : Option.None; 50 | 51 | static Either> Add5ToIt(Option input) 52 | { 53 | // arbitary test 54 | var isValid = input.Match(Some: number => number > 12, None: false); 55 | 56 | // We can return a IAmFailure or a Option 57 | if(!isValid) 58 | return new GenericFailure("must be greater than 12"); 59 | return input.Map(validInput => validInput + 5); //Return Option 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tutorial35_ThrowIfFailed()/Tutorial35 - ThrowIfFailed().csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial34\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial36_FailureToNone/CustomExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using LanguageExt; 6 | 7 | namespace Tutorial36 8 | { 9 | // Note a 'Successful' either is in the Right state while a 'unsucessful' either is in Left state 10 | public static class EitherExtensions 11 | { 12 | 13 | /// 14 | /// Throws an exception if the either is in left state ie failed 15 | /// 16 | public static TRight ThrowIfFailed(this Either either) where TLeft : IAmFailure 17 | => either.IfLeft(failure => throw new UnexpectedFailureException(failure)); 18 | 19 | //Given Either in Failure state (IAmFailure) to None, if the check on the failure fails - ie checking if IAmFailure is suitable 20 | public static Either> FailureToNone(this Either> item, Func predicate) 21 | => item.Match(rightValue => rightValue, failure => !predicate(failure) 22 | ? Prelude.Left>(failure) 23 | : Option.None, null); 24 | 25 | // Given Either with an optional Right state. If right state is none, make a failure using provided failure, otherwise use the valid value as the valid/right value of the Either 26 | public static Either NoneToFailure(this Either> item, Func failure) 27 | => item.Bind(e => e.Match(value => value.ToSuccess(), () => Prelude.Left(failure()))); 28 | 29 | /// 30 | /// Given An Either with an optional Right value. If option value is valid/some, make a Either Failure using provided failure, otherwise a Successful Either 31 | /// 32 | public static Either> SomeToFailure(this Either> item, Func failure) 33 | => item.Bind(either => either.Match(_ => Prelude.Left>(failure()), () => either.ToSuccess())); 34 | 35 | public static Func>, Either>> NoneToFailure(Func failure) 36 | => items => items.Sequence().Match(item => item.ToSuccess>(), () => Prelude.Left>(failure())); 37 | 38 | /// 39 | /// Make a Either in left(failure) state if the list is empty (use failure producing function provided), otherwise return the either in right state using the first item in the list as the light value in the either 40 | /// 41 | public static Either HeadOrFailure(this IEnumerable list, Func failure) 42 | => list.Match(() => Prelude.Left(failure()), s => Prelude.Right(s.First())); 43 | 44 | /// 45 | /// Make Either in right state 46 | /// 47 | public static Either ToSuccess(this T value) 48 | => Prelude.Right(value); 49 | 50 | // Make an IAmFailure to a Either ie convert a failure to a either that represents that failure ie an either in the left state 51 | public static Either ToEither(this IAmFailure failure) 52 | => Prelude.Left(failure); 53 | 54 | } 55 | 56 | public class UnexpectedFailureException : Exception 57 | { 58 | public UnexpectedFailureException(IAmFailure failure) 59 | { 60 | 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tutorial36_FailureToNone/IAmFailure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Tutorial36 6 | { 7 | public interface IAmFailure 8 | { 9 | string Reason { get; set; } 10 | } 11 | 12 | public class GenericFailure : IAmFailure 13 | { 14 | public GenericFailure(string reason) 15 | { 16 | Reason = reason; 17 | } 18 | 19 | public string Reason { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tutorial36_FailureToNone/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using LanguageExt; 8 | using LanguageExt.DataTypes.Serialisation; 9 | 10 | 11 | namespace Tutorial36 12 | { 13 | // We can convert a 'failed' standard wrapped function to a None. This is helpful if you want to turn a failure into a 'valid' standard function either but with None Right value 14 | class Program 15 | { 16 | static void Main(string[] args) 17 | { 18 | var result1 = 19 | from divideResult in DivideBy1(225, 66) 20 | from addResult in Add5ToIt1(divideResult).FailureToNone(failure => true) 21 | from calcResult in PerformPensionCalculations1(addResult).FailureToNone(failure => true) 22 | select calcResult; 23 | 24 | Console.WriteLine($"The result is '{result1.ThrowIfFailed()}'"); // this wont throw even though we failed, becasue we converted failures to None above! 25 | } 26 | 27 | static Either> PerformPensionCalculations1(Option input) 28 | { 29 | var isGreaterThan100 = input.Match(Some: number => number > 1, None: false); //match is like flattening an either in one common result type, in this case: bool 30 | return !isGreaterThan100 31 | ? (Either>) new GenericFailure("Must be greater than 100") 32 | : input.Map(CalculateYourPension); 33 | } 34 | 35 | static int PerformPensionCalculations(int input) 36 | => CalculateYourPension(input); 37 | 38 | static int CalculateYourPension(int input) 39 | => (input * 3) / 26; 40 | 41 | static int DivideBy(int thisNumber, int dividedByThatNumber) 42 | => dividedByThatNumber == 0 ? 0 : thisNumber / dividedByThatNumber; 43 | 44 | static Either> DivideBy1(int thisNumber, int dividedByThatNumber) => 45 | dividedByThatNumber == 0 46 | ? new GenericFailure("Can't divide by 0") 47 | : Option.Some(thisNumber / dividedByThatNumber).ToSuccess(); 48 | 49 | static Either> Add5ToIt1(Option input) 50 | { 51 | var isValid = input.Match(Some: number => number > 12, None: false); 52 | return !isValid 53 | ? (Either>) new GenericFailure("must be greater than 12") 54 | : input.Map(validInput => validInput + 5); 55 | } 56 | 57 | static int Add5ToIt(int input) 58 | => input + 5; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tutorial36_FailureToNone/Tutorial36 - FailureToNone.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial36\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial37_Caching_and_Memoization/Tutorial37 - Caching and Memoization.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial37\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/ChangeAgeEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Tutorial37 2 | { 3 | public class ChangeAgeEvent : Event 4 | { 5 | public ChangeAgeEvent(int newAge) 6 | { 7 | NewAge = newAge; 8 | EventDescrition = "\nContains inforamtion about changing a persons age"; 9 | EventName = "ChangeAgeEvent"; 10 | } 11 | 12 | public int NewAge { get; } 13 | 14 | public override Person ApplyEventTo(Person person) 15 | { 16 | Person updated = new Person(person); 17 | updated.Age = NewAge; 18 | updated.History.Add($"\nChanged age to " + NewAge); 19 | return updated; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/ChangeExpertiseEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Tutorial37 2 | { 3 | public class ChangeExpertiseEvent : Event 4 | { 5 | public string NewExpertise {get;set;} 6 | public ChangeExpertiseEvent(string newExpertise) 7 | { 8 | NewExpertise = newExpertise; 9 | } 10 | public override Person ApplyEventTo(Person person) 11 | { 12 | var updated = new Person(person); 13 | updated.Expertise = NewExpertise; 14 | updated.History.Add($"\nChanged Expertise to {NewExpertise}"); 15 | return updated; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/ChangeNameEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Tutorial37 2 | { 3 | public class ChangeNameEvent: Event 4 | { 5 | public ChangeNameEvent(string name) 6 | { 7 | NewName = name; 8 | EventName = "ChangedNameEvent"; 9 | EventDescrition = "Changes a Persons name"; 10 | } 11 | 12 | public string NewName { get; } 13 | 14 | public override Person ApplyEventTo(Person person) 15 | { 16 | Person updated = new Person(person); 17 | updated.Name = NewName; 18 | updated.History.Add($"\nChanged name to {NewName}"); 19 | return updated; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/ChangeRoleEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Tutorial37 2 | { 3 | public class ChangeRoleEvent : Event 4 | { 5 | public ChangeRoleEvent(string newRole) 6 | { 7 | NewRole = newRole; 8 | } 9 | 10 | public string NewRole { get; } 11 | 12 | public override Person ApplyEventTo(Person person) 13 | { 14 | var updated = new Person(person); 15 | updated.Role = NewRole; 16 | updated.History.Add($"\nChanged Role to {NewRole}"); 17 | return updated; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/Event.cs: -------------------------------------------------------------------------------- 1 | namespace Tutorial37 2 | { 3 | public abstract class Event 4 | { 5 | public string EventName {get;set;} 6 | public string EventDescrition {get;set;} 7 | public abstract T ApplyEventTo(T thing); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/Person.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | 4 | namespace Tutorial37 5 | { 6 | public class Person 7 | { 8 | public int Age {get;set;} = 0; 9 | public string Name {get;set;} = "NoNameSet"; 10 | public string Expertise {get;set;} = "NoExpertiseSet"; 11 | public string Role {get;set;} = "NoRoleSet"; 12 | public Person() 13 | => Age = 0; 14 | 15 | public Person(Person p) : this() 16 | { 17 | Age = p.Age; 18 | History = p.History; 19 | Name = p.Name; 20 | Expertise = p.Expertise; 21 | Role = p.Role; 22 | } 23 | 24 | public List History = new List(); 25 | public override string ToString() 26 | => $"Person's name is {Name} and is {Age} years old now. Expertise is '{Expertise}', while Role is '{Role}'\nPrevious state chnges were: {string.Join(',', History)}"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Threading.Tasks; 8 | using LanguageExt; 9 | using LanguageExt.DataTypes.Serialisation; 10 | 11 | 12 | namespace Tutorial37 13 | { 14 | 15 | // This tutorial shows how you can use the Fold() function in languageExt to change the state of an object over time 16 | class Program 17 | { 18 | static void Main(string[] args) 19 | { 20 | List years = new List 21 | { 22 | 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 23 | 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 24 | 2015, 2016, 2017, 2018, 2019 25 | }; 26 | 27 | List> events = new List> 28 | { 29 | new ChangeNameEvent("Stuart"), 30 | new ChangeExpertiseEvent("Programming"), 31 | new ChangeNameEvent("Stuart Mathews"), 32 | new ChangeExpertiseEvent("Running"), 33 | new ChangeAgeEvent(33), 34 | }; 35 | 36 | Person person = new Person(); 37 | 38 | // A state (InitialResult) changes over time and it changes using results of the previous change. It uses an item from the array in changing the state each time. 39 | // The state changes the number of elements in the IEnumerable 40 | // For a Lst which has multiple items in it, the state will change that many times 41 | 42 | // NB: years represent the state changes that will occur 43 | var changedPerson = years.Fold(/*initial state*/ person, (previousResult, year) => ChangeState(year, previousResult)); 44 | 45 | // Apply some events to the person over time 46 | changedPerson = events.Fold(person, (previousResult, evt) => evt.ApplyEventTo(previousResult)); 47 | 48 | // View the changed person 49 | Console.WriteLine($"{changedPerson}"); 50 | 51 | // local function 52 | Person ChangeState(int year, Person previousResult) 53 | { 54 | Person updatedPerson = new Person(previousResult); 55 | updatedPerson.History.Add($"\nIn {year}, this person was {updatedPerson.Age} years old"); 56 | updatedPerson.Age++; 57 | return updatedPerson; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tutorial38_Changing_state_over_time/Tutorial38 - Changing state over time.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial38\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial39_Immutability_Smart_Constructors/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using LanguageExt; 3 | 4 | 5 | namespace Tutorial39 6 | { 7 | /// 8 | /// This is an immutable object because: 9 | /// a) Has private setters and that means its state cannot be changed after creation 10 | /// b) All properties are immutable - either strings or native types or System.Collections.Immutable types 11 | /// c) Any change that needs to take place must result in a newly create object of this type 12 | /// 13 | public class Person 14 | { 15 | public string FirstName { get; } 16 | public string LastName { get; } 17 | 18 | /// 19 | /// Notice this is a private constructor so a Person cannot be created normally...there must be another way in! 20 | /// It must be created indirectly via .Of() or .New() which then can use the private constructor below 21 | /// 22 | /// the persons first name 23 | /// The persons last name 24 | private Person(string firstName, string lastName) 25 | { 26 | if (!IsValid(firstName, lastName)) 27 | { 28 | throw new ArgumentException("Invalid input"); 29 | } 30 | FirstName = firstName; 31 | LastName = lastName; 32 | } 33 | public override string ToString() 34 | => $"{FirstName} {LastName}"; 35 | 36 | /// 37 | /// This pretends to be a constructor by being the only method allowed to call the constructor. 38 | /// The actual and real constructor is only called if input is valid to this function 39 | /// As a result it gaurentees no exceptions can ever be thrown during creation of the object through 40 | /// this method. 41 | /// 42 | /// Persons first name 43 | /// Persons last name 44 | /// 45 | public static Option Of(string firstName, string lastName) 46 | => IsValid(firstName, lastName) 47 | ? Option .Some(new Person(firstName, lastName)) 48 | : Option .None; 49 | 50 | private static bool IsValid(string firstName, string lastName) 51 | => !string.IsNullOrEmpty(firstName) && !string.IsNullOrEmpty(lastName); 52 | 53 | // Instance method allows this function to be chained. 54 | // With Helper to make object creation easier as properties of the object change 55 | public Option Rename(string firstName = null, string lastName = null) 56 | => Of(firstName, lastName); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Tutorial39_Immutability_Smart_Constructors/PersonExtensions.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | 3 | namespace Tutorial39 4 | { 5 | // Extension methods allow us to chain non-instance method calls, 6 | public static class PersonExtensions 7 | { 8 | /// 9 | /// Functional because 10 | /// A) it does not call any non-pure functions or use any other data than the arguments(with are immutable) passed to it. 11 | /// B) functions should be declared separately from the data they act upon, like it is 12 | /// C) Creates a new Object 13 | /// 14 | /// 15 | /// 16 | /// 17 | public static Option Rename(this Option person, string firstName, string lastName) 18 | { 19 | return person.Bind(per => per.Rename(firstName, lastName)); 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Tutorial39_Immutability_Smart_Constructors/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Threading.Tasks; 8 | using LanguageExt.DataTypes.Serialisation; 9 | 10 | 11 | namespace Tutorial39 12 | { 13 | // This tutorial demonstrates creating immutable objects using Smart Constructors, and using them as you would use any other OOP objects. 14 | // However, this object is immutable in as much as its specifically designed not to have its state changed by operations 15 | // The operations that it does have, create a new object with the modification and leaves the original object untouched. 16 | class Program 17 | { 18 | static void Main(string[] args) 19 | { 20 | // Note we cannot create a person through a conventional constructor 21 | // we use a smart constructor ie Of() to perform validation on construction and return an Option depending the construction 22 | // of the person is valid - no exceptions are thrown when they are not - which is usually what happens with normal constructors. 23 | 24 | var originalMe = Person.Of("Steward", "Mathews"); 25 | 26 | 27 | // Perform a state change on the person, however we enforce creation of a new copy of person, pre-initialized with previous data. 28 | // We therefore don't modify state - particularly of the person that we are about to modify - this is not a member function that changes te object as is traditionally done 29 | var changedMe = originalMe.Bind(person => person.Rename("Steward Rob Charles", "Mathews")); // Remember we're binidng here as Rename returns a Monad already if it didn't we'd use Map() 30 | 31 | // We can do this declaratively also 32 | var stateChangedMe1 = from person in originalMe 33 | from renamedperson in person.Rename("Steward Rob Charles", "Mathews") 34 | select renamedperson; 35 | 36 | // We can also chain the modifications using the extension method 37 | 38 | var lastChange = changedMe 39 | .Rename("Stuart", "Mathews") 40 | .Rename("Stuart Robert Charles", "Mathews"); 41 | 42 | Console.WriteLine($"First I was {originalMe}, then ultimately I was {lastChange}"); 43 | 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Tutorial39_Immutability_Smart_Constructors/Tutorial39 - Immutability Smart Constructors.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial37\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial40_Try/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using LanguageExt; 3 | 4 | namespace Tutorial40 5 | { 6 | 7 | public static class Extensions 8 | { 9 | /// 10 | /// Allows you to express any value as an Either 11 | /// 12 | /// The left value - usually a failure representative 13 | /// the right value - usually a success representative 14 | /// 15 | /// 16 | public static Either ToEither(this R value) 17 | => Prelude.Right(value); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Tutorial40_Try/ExternalLibraryFailure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tutorial40 4 | { 5 | public class ExternalLibraryFailure : IAmFailure 6 | { 7 | public ExternalLibraryFailure(Exception exception) 8 | { 9 | Reason = exception.Message; 10 | Exception = exception; 11 | } 12 | 13 | public ExternalLibraryFailure(string message) 14 | { 15 | Reason = message; 16 | } 17 | 18 | public string Reason { get; set; } 19 | public Exception Exception { get; } 20 | 21 | public static IAmFailure Create(string message) => new ExternalLibraryFailure(message); 22 | public static IAmFailure Create(Exception e) => new ExternalLibraryFailure(e); 23 | } 24 | } -------------------------------------------------------------------------------- /Tutorial40_Try/IAmFailure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Tutorial40 6 | { 7 | public interface IAmFailure 8 | { 9 | string Reason { get; set; } 10 | } 11 | 12 | public class GenericFailure : IAmFailure 13 | { 14 | public GenericFailure(string reason) 15 | { 16 | Reason = reason; 17 | } 18 | 19 | public string Reason { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tutorial40_Try/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Metadata.Ecma335; 5 | using System.Runtime.CompilerServices; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Threading.Tasks; 8 | using LanguageExt; 9 | using LanguageExt.DataTypes.Serialisation; 10 | 11 | 12 | namespace Tutorial40 13 | { 14 | // This tutorial demonstrates the use of the Try<> Monad. 15 | class Program 16 | { 17 | static void Main(string[] args) 18 | { 19 | // This is an function that could throw an exception. We dont want that because that would jump straight out of our function 20 | // and cause our function not to complete fully. We want robust, dependable functions that always return no matter what. 21 | int SomeExternalCode(int numerator, int denominator) 22 | { 23 | if(denominator == 0) 24 | throw new DivideByZeroException(); 25 | return numerator / denominator; 26 | } 27 | 28 | // What we can do now is wrap this unsafe function, into a Try<> which will capture any exceptions that are thrown (they dont leave the function) 29 | Try try1 = new Try(() => 30 | { 31 | var result = SomeExternalCode(25, 5); 32 | return result; 33 | }); 34 | 35 | Try try2 = new Try(() => 36 | { 37 | var result = SomeExternalCode(25, 0); 38 | return result; 39 | }); 40 | 41 | // Now we have encapsulated any exceptions into the Try<> type, we can now check if it had any failures otherwise use the result 42 | var result1 = try1.Match( 43 | unit => unit.ToEither(), // was ok, no exceptions thrown 44 | exception => new ExternalLibraryFailure(exception)); // We got an exception, now how should we deal with it - lets turn it into a IAmFailure type 45 | 46 | var result2 = try2.Match( 47 | unit => unit.ToEither(), // was ok, no exceptions thrown 48 | exception => new ExternalLibraryFailure(exception)); // We got an exception, now how should we deal with it - lets turn it into a IAmFailure type 49 | 50 | // Print the results 51 | Console.WriteLine($"Result1 was: {result1} and Result2 was {result2}"); 52 | 53 | // And if you required all/both to succeed you could use short-circuiting behavior in a pipeline 54 | var overallResult = 55 | from res1 in result1 56 | from res2 in result2 57 | select res1 / res2; 58 | 59 | Console.WriteLine($"The combined result is {overallResult}"); 60 | } 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /Tutorial40_Try/Tutorial40 - Try.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial37\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/AggregatePipelineFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tutorial41 7 | { 8 | public class AggregatePipelineFailure : IAmFailure 9 | { 10 | public AggregatePipelineFailure(IEnumerable failures) 11 | { 12 | frequencies = new Dictionary(); 13 | var failureNames = failures.GroupBy(o => o.GetType().Name + o.Reason); 14 | var sb = new StringBuilder(); 15 | foreach (var name in failureNames) 16 | { 17 | var lookupFailure = failures.First(o=>(o.GetType().Name + o.Reason).Equals(name.Key)); 18 | if(!frequencies.ContainsKey(lookupFailure)) 19 | frequencies.Add(lookupFailure, name.Count()); 20 | 21 | sb.Append($"Failure name: {name.Key} Count: {name.Count()}\n"); 22 | } 23 | 24 | Reason = sb.ToString(); 25 | } 26 | public string Reason { get; set; } 27 | public Dictionary frequencies {get;set;} 28 | public static IAmFailure Create(IEnumerable failures) => new AggregatePipelineFailure(failures); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/ConditionNotSatisfiedFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Tutorial41 3 | { 4 | public class ConditionNotSatisfiedFailure : IAmFailure 5 | { 6 | public ConditionNotSatisfiedFailure(string caller = "Caller not captured") 7 | { 8 | Reason = $"Condition not satisfied. Caller: {caller}"; 9 | } 10 | public string Reason { get; set; } 11 | public static IAmFailure Create(string caller) => new ConditionNotSatisfiedFailure(caller); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/ExceptionFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | using System; 4 | 5 | namespace Tutorial41 6 | { 7 | public class ExceptionFailure : Exception, IAmFailure 8 | { 9 | public ExceptionFailure(Exception e) => Reason = e.Message; 10 | public string Reason { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/ExternalLibraryFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | 4 | namespace Tutorial41 5 | { 6 | public class ExternalLibraryFailure : IAmFailure 7 | { 8 | public ExternalLibraryFailure(Exception exception) 9 | { 10 | Reason = exception.Message; 11 | Exception = exception; 12 | } 13 | 14 | public ExternalLibraryFailure(string message) 15 | { 16 | Reason = message; 17 | } 18 | 19 | public string Reason { get; set; } 20 | public Exception Exception { get; } 21 | 22 | public static IAmFailure Create(string message) => new ExternalLibraryFailure(message); 23 | public static IAmFailure Create(Exception e) => new ExternalLibraryFailure(e); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/IAmFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Tutorial41 3 | { 4 | /// 5 | /// Represents a failure for some reason 6 | /// 7 | public interface IAmFailure 8 | { 9 | /// 10 | /// Nature of the failure 11 | /// 12 | string Reason { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/InvalidDataFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Tutorial41 3 | { 4 | public class InvalidDataFailure : IAmFailure 5 | { 6 | public InvalidDataFailure(string empty) 7 | { 8 | Reason = empty; 9 | } 10 | 11 | public string Reason { get; set; } 12 | public static IAmFailure Create(string message) => new InvalidDataFailure(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/NotFound.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Tutorial41 3 | { 4 | public class NotFound : IAmFailure 5 | { 6 | public NotFound(string message) 7 | { 8 | Reason = message; 9 | } 10 | public string Reason { get; set; } 11 | 12 | public static IAmFailure Create(string message) => new NotFound(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/NotTypeExceptionFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | 4 | namespace Tutorial41 5 | { 6 | public class NotTypeExceptionFailure : IAmFailure 7 | { 8 | public NotTypeExceptionFailure(Type type) 9 | { 10 | Reason = $"Function did not return expected type of '{type}'"; 11 | } 12 | 13 | public string Reason { get; set; } 14 | 15 | public static IAmFailure Create(Type type) => new NotTypeExceptionFailure(type); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/ShortCircuitFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Tutorial41 3 | { 4 | public class ShortCircuitFailure : IAmFailure 5 | { 6 | public ShortCircuitFailure(string message) 7 | { 8 | Reason = message; 9 | } 10 | 11 | public string Reason { get; set; } 12 | public static IAmFailure Create(string msg) => new ShortCircuitFailure(msg); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/TransformExceptionFailure.cs: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------- 2 | 3 | // 4 | 5 | // Copyright (c) Stuart Mathews. All rights reserved. 6 | 7 | // Stuart Mathews 8 | 9 | // 03/10/2021 13:16:11 10 | 11 | // 12 | 13 | //----------------------------------------------------------------------- 14 | 15 | using System; 16 | 17 | namespace Tutorial41 18 | { 19 | public class TransformExceptionFailure : IAmFailure 20 | { 21 | public string Reason { get; set; } 22 | 23 | public Exception Exception { get; set; } 24 | 25 | public TransformExceptionFailure(string message) 26 | { 27 | Reason = message; 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return Reason; 33 | } 34 | 35 | public static IAmFailure Create(string msg) => new TransformExceptionFailure(msg); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/UnexpectedFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Tutorial41 3 | { 4 | public class UnexpectedFailure : IAmFailure 5 | { 6 | public UnexpectedFailure(string reason) 7 | { 8 | Reason = reason; 9 | } 10 | 11 | public override string ToString() 12 | { 13 | return Reason; 14 | } 15 | 16 | public string Reason { get; set; } 17 | public static IAmFailure Create(string reason) => new UnexpectedFailure(reason); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/UnexpectedFailureException.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | 4 | namespace Tutorial41 5 | { 6 | public class UnexpectedFailureException : Exception, IAmFailure 7 | { 8 | public UnexpectedFailureException(IAmFailure failure): base(failure.Reason) 9 | { 10 | Reason = failure.Reason; 11 | } 12 | 13 | public string Reason { get; set; } 14 | public static IAmFailure Create(IAmFailure failure) => new UnexpectedFailureException(failure); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Failures/UninitializedFailure.cs: -------------------------------------------------------------------------------- 1 | 2 | using LanguageExt; 3 | 4 | namespace Tutorial41 5 | { 6 | internal class UninitializedFailure : IAmFailure 7 | { 8 | public UninitializedFailure(string what) 9 | { 10 | Reason = what; 11 | } 12 | public string Reason { get; set; } 13 | public static IAmFailure Create(string what) => new UninitializedFailure(what); 14 | public static Either Create(string what) => new UninitializedFailure(what); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Program.cs: -------------------------------------------------------------------------------- 1 | using LanguageExt; 2 | using System; 3 | 4 | namespace Tutorial41 5 | { 6 | // This tutorial shows you how to protect binding transformations from exceptions, 7 | // and also override how the problem is surfaced to the user. 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | // Create some products based on an assembly line (pipeline) 13 | var product1 = GetAnalysisResult() 14 | .Bind(analysisResult => GetDesignResult()) 15 | .Bind(designResult => GetImplementationResult()); 16 | 17 | // Design fails internally by throwing an exception: 18 | var product2 = GetAnalysisResult() 19 | .EnsuringBind(analysisResult => GetDesignResult(throwException: true)) // We can Ensure the transformation function against exceptions 20 | .Bind(designResult => GetImplementationResult()); // We assume there aren't any exceptions here 21 | 22 | // Implementation fails internally by throwing an exception: 23 | var product3 = GetAnalysisResult() 24 | .Bind(analysisResult => GetDesignResult()) 25 | .EnsuringBind(designResult => GetImplementationResult(throwException: true)); 26 | 27 | 28 | // However sometimes the internal error caused somewhere in the guts of the function isn't as useful, 29 | // and re-interpreting it to add more context at a higher level is more useful... 30 | 31 | // Same process but you can override how internal error is surfaced/presented using MapLeft: 32 | 33 | var product4 = GetAnalysisResult() 34 | .EnsuringBind(analysisResult => GetDesignResult(throwException: true)) 35 | // Whatever the left value is, override/customize/surface it diffirently as a new Failure and add better context, ie product4 36 | .MapLeft( failure => UnexpectedFailure.Create($"Design Error Occured while creating product 4. Details were '{failure.Reason}'")) 37 | .Bind(designResult => GetImplementationResult()); 38 | var product5 = GetAnalysisResult() 39 | .Bind(analysisResult => GetDesignResult()) 40 | .EnsuringBind(designResult => GetImplementationResult(throwException: true)) 41 | // Whatever the left value is, override/customize/surface it diffirently as a new Failure and add better context, ie product5 42 | .MapLeft( failure => UnexpectedFailure.Create($"Implementation Error Occured while creating product 5. Details were '{failure.Reason}'")); 43 | 44 | Console.WriteLine($"Product1 result: {product1}"); 45 | Console.WriteLine($"Product2 result: {product2}"); 46 | Console.WriteLine($"Product3 result: {product3}"); 47 | Console.WriteLine($"Product4 result: {product4}"); 48 | Console.WriteLine($"Product5 result: {product5}"); 49 | 50 | 51 | } 52 | 53 | /// 54 | /// Do some Analysis Work 55 | /// 56 | /// 57 | public static Either GetAnalysisResult() 58 | { 59 | // Do some analysis work... 60 | return "Analysis Success"; 61 | } 62 | 63 | /// 64 | /// Do some design work 65 | /// 66 | /// 67 | /// 68 | public static Either GetDesignResult(bool throwException = false) 69 | { 70 | // Do some design work... 71 | return throwException 72 | ? throw new UnexpectedFailureException(UnexpectedFailure.Create("Design Width is too large. Error DES623 Ref=KJE871")) 73 | : "Design Success"; 74 | } 75 | 76 | /// 77 | /// Do some implementation 78 | /// 79 | /// 80 | /// 81 | public static Either GetImplementationResult(bool throwException = false) 82 | { 83 | // Do some implemantion work... 84 | return throwException 85 | ? throw new UnexpectedFailureException(UnexpectedFailure.Create("Implementation Volume exceeds mainframe size. Error IMP371 Ref=K763P")) 86 | : "Implementation Success"; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Tutorial41_Statics/Tutorial41 - Dealing with errors.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.1 5 | 40 6 | C:\Users\Stuart\git\LanguageExtForNewDevelopers\Backup\Tutorial37\ 7 | 2.0 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /UnderstandingLanguageExtTutorial.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pandoc -s --metadata pagetitle="The LanguageExt Tutorial" --highlight-style tango --filter pandoc-include-code tutorial.md -o tutorial1.html 3 | -------------------------------------------------------------------------------- /myMediaFolder/media/image10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image10.png -------------------------------------------------------------------------------- /myMediaFolder/media/image101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image101.png -------------------------------------------------------------------------------- /myMediaFolder/media/image103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image103.png -------------------------------------------------------------------------------- /myMediaFolder/media/image105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image105.png -------------------------------------------------------------------------------- /myMediaFolder/media/image107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image107.png -------------------------------------------------------------------------------- /myMediaFolder/media/image109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image109.png -------------------------------------------------------------------------------- /myMediaFolder/media/image111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image111.png -------------------------------------------------------------------------------- /myMediaFolder/media/image113.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image113.png -------------------------------------------------------------------------------- /myMediaFolder/media/image115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image115.png -------------------------------------------------------------------------------- /myMediaFolder/media/image117.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image117.png -------------------------------------------------------------------------------- /myMediaFolder/media/image119.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image119.png -------------------------------------------------------------------------------- /myMediaFolder/media/image12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image12.png -------------------------------------------------------------------------------- /myMediaFolder/media/image121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image121.png -------------------------------------------------------------------------------- /myMediaFolder/media/image123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image123.png -------------------------------------------------------------------------------- /myMediaFolder/media/image125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image125.png -------------------------------------------------------------------------------- /myMediaFolder/media/image127.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image127.png -------------------------------------------------------------------------------- /myMediaFolder/media/image14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image14.png -------------------------------------------------------------------------------- /myMediaFolder/media/image15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image15.png -------------------------------------------------------------------------------- /myMediaFolder/media/image17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image17.png -------------------------------------------------------------------------------- /myMediaFolder/media/image18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image18.png -------------------------------------------------------------------------------- /myMediaFolder/media/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image2.png -------------------------------------------------------------------------------- /myMediaFolder/media/image20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image20.png -------------------------------------------------------------------------------- /myMediaFolder/media/image23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image23.png -------------------------------------------------------------------------------- /myMediaFolder/media/image25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image25.png -------------------------------------------------------------------------------- /myMediaFolder/media/image27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image27.png -------------------------------------------------------------------------------- /myMediaFolder/media/image29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image29.png -------------------------------------------------------------------------------- /myMediaFolder/media/image31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image31.png -------------------------------------------------------------------------------- /myMediaFolder/media/image33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image33.png -------------------------------------------------------------------------------- /myMediaFolder/media/image35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image35.png -------------------------------------------------------------------------------- /myMediaFolder/media/image37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image37.png -------------------------------------------------------------------------------- /myMediaFolder/media/image39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image39.png -------------------------------------------------------------------------------- /myMediaFolder/media/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image4.png -------------------------------------------------------------------------------- /myMediaFolder/media/image41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image41.png -------------------------------------------------------------------------------- /myMediaFolder/media/image43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image43.png -------------------------------------------------------------------------------- /myMediaFolder/media/image45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image45.png -------------------------------------------------------------------------------- /myMediaFolder/media/image48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image48.png -------------------------------------------------------------------------------- /myMediaFolder/media/image49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image49.png -------------------------------------------------------------------------------- /myMediaFolder/media/image51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image51.png -------------------------------------------------------------------------------- /myMediaFolder/media/image53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image53.png -------------------------------------------------------------------------------- /myMediaFolder/media/image55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image55.png -------------------------------------------------------------------------------- /myMediaFolder/media/image57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image57.png -------------------------------------------------------------------------------- /myMediaFolder/media/image59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image59.png -------------------------------------------------------------------------------- /myMediaFolder/media/image6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image6.png -------------------------------------------------------------------------------- /myMediaFolder/media/image61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image61.png -------------------------------------------------------------------------------- /myMediaFolder/media/image63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image63.png -------------------------------------------------------------------------------- /myMediaFolder/media/image65.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image65.png -------------------------------------------------------------------------------- /myMediaFolder/media/image67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image67.png -------------------------------------------------------------------------------- /myMediaFolder/media/image69.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image69.png -------------------------------------------------------------------------------- /myMediaFolder/media/image71.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image71.png -------------------------------------------------------------------------------- /myMediaFolder/media/image73.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image73.png -------------------------------------------------------------------------------- /myMediaFolder/media/image75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image75.png -------------------------------------------------------------------------------- /myMediaFolder/media/image77.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image77.png -------------------------------------------------------------------------------- /myMediaFolder/media/image79.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image79.png -------------------------------------------------------------------------------- /myMediaFolder/media/image8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image8.png -------------------------------------------------------------------------------- /myMediaFolder/media/image81.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image81.png -------------------------------------------------------------------------------- /myMediaFolder/media/image83.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image83.png -------------------------------------------------------------------------------- /myMediaFolder/media/image85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image85.png -------------------------------------------------------------------------------- /myMediaFolder/media/image87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image87.png -------------------------------------------------------------------------------- /myMediaFolder/media/image89.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image89.png -------------------------------------------------------------------------------- /myMediaFolder/media/image91.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image91.png -------------------------------------------------------------------------------- /myMediaFolder/media/image93.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image93.png -------------------------------------------------------------------------------- /myMediaFolder/media/image95.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image95.png -------------------------------------------------------------------------------- /myMediaFolder/media/image97.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image97.png -------------------------------------------------------------------------------- /myMediaFolder/media/image99.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stumathews/UnderstandingLanguageExt/2ddff3f4773665c9805d1d8ae2fa12741a16ff0e/myMediaFolder/media/image99.png -------------------------------------------------------------------------------- /rename.bat: -------------------------------------------------------------------------------- 1 | REM See original at: https://stackoverflow.com/questions/52067917/how-to-replace-all-spaces-by-underscores-in-all-file-and-folder-names-recursivel 2 | 3 | @echo off 4 | setlocal EnableExtensions DisableDelayedExpansion 5 | set "StartFolder=C:\Users\cex\source\repos\UnderstandingLanguageExt" 6 | 7 | cd /D %SystemRoot% 8 | set "RenameError=" 9 | 10 | rem Rename all files containing at least one space character in file name. 11 | for /F "delims=" %%I in ('dir "%StartFolder%\* *" /A-D /B /S 2^>nul') do call :RenameFile "%%I" 12 | 13 | rem Rename all folders containing at least one space character in folder name. 14 | for /F "delims=" %%I in ('dir "%StartFolder%\* *" /AD /B /S 2^>nul') do call :RenameFolder "%%I" 15 | 16 | if defined RenameError echo/& pause 17 | rem Restore initial environment and exit this batch file. 18 | endlocal 19 | goto :EOF 20 | 21 | 22 | :RenameFile 23 | set "NewFileName=%~nx1" 24 | set "NewFileName=%NewFileName: =_%" 25 | 26 | set "FileAttributes=%~a1" 27 | if "%FileAttributes:~3,1%" == "h" %SystemRoot%\System32\attrib.exe -h %1 28 | 29 | ren %1 "%NewFileName%" 2>nul 30 | if errorlevel 1 goto ErrorFileRename 31 | 32 | if "%FileAttributes:~3,1%" == "h" %SystemRoot%\System32\attrib.exe +h "%~dp1%NewFileName%" 33 | goto :EOF 34 | 35 | :ErrorFileRename 36 | echo Error renaming file %1 37 | set "RenameError=1" 38 | if "%FileAttributes:~3,1%" == "h" %SystemRoot%\System32\attrib.exe +h %1 39 | goto :EOF 40 | 41 | 42 | :RenameFolder 43 | set "NewFolderName=%~nx1" 44 | set "NewFolderName=%NewFolderName: =_%" 45 | 46 | set "FolderPath=%~dp1" 47 | if not exist "%FolderPath%" set "FolderPath=%FolderPath: =_%" 48 | set "FullFolderName=%FolderPath%%~nx1" 49 | if not exist "%FullFolderName%\" set "RenameError=1" & goto :EOF 50 | 51 | for %%J in ("%FullFolderName%") do set "FolderAttributes=%%~aJ" 52 | if "%FolderAttributes:~3,1%" == "h" %SystemRoot%\System32\attrib.exe -h "%FullFolderName%" 53 | 54 | ren "%FullFolderName%" "%NewFolderName%" 2>nul 55 | if errorlevel 1 goto ErrorFolderRename 56 | 57 | if "%FolderAttributes:~3,1%" == "h" %SystemRoot%\System32\attrib.exe +h "%FolderPath%%NewFolderName%" 58 | goto :EOF 59 | 60 | :ErrorFolderRename 61 | echo Error renaming folder "%FullFolderName%" 62 | set "RenameError=1" 63 | if "%FolderAttributes:~3,1%" == "h" %SystemRoot%\System32\attrib.exe +h "%FullFolderName%" 64 | goto :EOF 65 | --------------------------------------------------------------------------------