Last active
September 26, 2021 05:49
-
-
Save eramax/e90890d93f8af6779f0dc2bebbecdac2 to your computer and use it in GitHub Desktop.
Functional Programming with C# - Simon Painter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Functional Programming with C# - Simon Painter - NDC Oslo 2020 | |
/// https://www.youtube.com/watch?v=gvyTB4aMI4o | |
/// </summary> | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text.Json.Serialization; | |
using System.Text.RegularExpressions; | |
public class Program | |
{ | |
public static void Main() | |
{ | |
ex1(); | |
ex2(); | |
ex3(); | |
ex4(); | |
ex5(); | |
ex6(); | |
ex7(); | |
} | |
record Employee(string Name, DateTime birth_date); | |
static void ex1() | |
{ | |
Func<Employee, bool> rule1 = x => x.Name != string.Empty; | |
Func<Employee, bool> rule2 = x => new Regex("[a-zA-Z]").IsMatch(x.Name); | |
var ahmed = new Employee("Ahmed", DateTime.Now); | |
// validate | |
var result = new[] { rule1, rule2 }.All(r => r(ahmed)); | |
Console.WriteLine($"Result = {result}"); | |
Console.WriteLine($"Result2 = {ahmed.Validate(rule1, rule2)}"); | |
} | |
//record Person([property: JsonPropertyName("firstName")] string FirstName, | |
// [property: JsonPropertyName("lastName")] string LastName); | |
abstract record Person(string FirstName, string LastName); | |
record Teacher(string FirstName, string LastName, int Grade) : Person(FirstName, LastName); | |
record Student(string FirstName, string LastName, int Grade) : Person(FirstName, LastName); | |
static void ex2() | |
{ | |
Person teacher = new Teacher("Ahmed", "Morsi", 3); | |
Person student = new Student("Ali", "Moh", 62); | |
static string test(Person p) => p switch | |
{ | |
Teacher => "Teacher", | |
Student x => x.Grade switch | |
{ | |
0 => "Failed", | |
< 50 => "Less than 50%", | |
< 70 => "Less than 70%", | |
< 90 => "Less than 90%", | |
_ => "Excellent" | |
}, | |
_ => "" | |
}; | |
Console.WriteLine($"student is {test(student)}"); | |
Console.WriteLine($"teacher is {test(teacher)}"); | |
} | |
static void ex3() | |
{ | |
static decimal ApplayTaxToSalary(decimal salary) => | |
new (Func<decimal, bool> condition, Func<decimal, decimal> calculator)[] | |
{ | |
(x => x <= 1000, x => x), | |
(x => x <= 2000, x => x * 0.9M), | |
(x => x <= 5000, x => x * 0.8M), | |
(x => x > 5000, x => x * 0.7M) | |
}.First(x => x.condition(salary)).calculator(salary); | |
Console.WriteLine($"After Tax for 2000 = {ApplayTaxToSalary(2000)}"); | |
Console.WriteLine($"After Tax for 1000 = {ApplayTaxToSalary(1000)}"); | |
Console.WriteLine($"After Tax for 4000 = {ApplayTaxToSalary(4000)}"); | |
Console.WriteLine($"After Tax for 8000 = {ApplayTaxToSalary(8000)}"); | |
} | |
static void ex4() | |
{ | |
var input = "aaaaabbbbbcdddbbbdddcccaaa"; | |
var output = input.Fork(x => x.Sum(), | |
x => x.Count(y => y == 'a'), | |
x => x.Count(y => y == 'b'), | |
x => x.Count(y => y == 'c'), | |
x => x.Count(y => y == 'd') | |
); | |
Console.WriteLine($"Sum of aaaaabbbbbcdddbbbdddcccaaa = {output}"); | |
} | |
/// <summary> | |
/// clojure | |
/// </summary> | |
static void ex5() | |
{ | |
Func<int, int> add(int x) => y => x + y; | |
var f = add(100); | |
Console.WriteLine($"clojure of 100 function = {f(5)}"); | |
} | |
static void ex6() | |
{ | |
Func<decimal, Func<decimal, decimal>> _add = x => y => x + y; | |
Func<decimal, Func<decimal, decimal>> _subtract = x => y => y - x; | |
Func<decimal, Func<decimal, decimal>> _multiply = x => y => x * y; | |
Func<decimal, Func<decimal, decimal>> _divide = x => y => y / x; | |
string CelsiusToFahrenheit(decimal input) => | |
input.Map(_multiply(9)) | |
.Map(_divide(5)) | |
.Map(_add(32)) | |
.Map(x => Math.Round(x, 2)) | |
.Map(x => $"{x}°F"); | |
Console.WriteLine($"CelsiusToFahrenheit of 37 = {CelsiusToFahrenheit(37)}"); | |
} | |
/// <summary> | |
/// Monad - pipeline | |
/// </summary> | |
static void ex7() | |
{ | |
Func<int, Person> GetPerson = (int personId) => personId switch | |
{ | |
1 => new Student("ahmed", "Morsi", 70), | |
_ => throw new Exception("Arrgh!") | |
}; | |
string formattedPerson(int personId) => personId.ToEither() | |
.Bind(GetPerson) | |
.Bind(x => $"{x.FirstName} {x.LastName}") | |
.Bind(x => x.Replace("a", "4")) | |
.Bind(x => x.Replace("e", "3")) | |
.Bind(x => x.Replace("i", "1")) | |
.Bind(x => x.Replace("o", "0")); | |
Console.WriteLine($"formattedPerson1 = {formattedPerson(1)}"); | |
Console.WriteLine($"formattedPerson2 = {formattedPerson(2)}"); | |
} | |
} | |
public static class Ext | |
{ | |
public static bool Validate<T>(this T @this, params Func<T, bool>[] predicates) => predicates.All(x => x(@this)); | |
public static TTo Map<TFrom, TTo>(this TFrom @this, Func<TFrom, TTo> f) => f(@this); | |
public static TOut Fork<TIn, TOut>(this TIn @this, Func<IEnumerable<TOut>, TOut> aggregate, params Func<TIn, TOut>[] prongs) | |
=> prongs.Select(f => f(@this)).Map(aggregate); | |
public abstract class Either<T> | |
{ | |
public abstract T Value { get; } | |
public static implicit operator T(Either<T> @this) => @this.Value; | |
} | |
public class Right<T> : Either<T> | |
{ | |
public override T Value { get; } | |
public Right(T val) | |
{ | |
Value = val; | |
} | |
} | |
public class Left<T> : Either<T> | |
{ | |
public override T Value => default(T); | |
public Exception Exception { get; set; } | |
public Left(Exception e) | |
{ | |
Exception = e; | |
} | |
} | |
public static Either<T> ToEither<T>(this T value) => new Right<T>(value); | |
public static Either<TToType> Bind<TFromType, TToType>(this Either<TFromType> @this, Func<TFromType, TToType> f) | |
{ | |
switch (@this) | |
{ | |
case Right<TFromType> rgt when !EqualityComparer<TFromType>.Default.Equals(rgt.Value, default(TFromType)): | |
try | |
{ | |
return f(rgt.Value).ToEither(); | |
} | |
catch (Exception e) | |
{ | |
return new Left<TToType>(e); | |
} | |
case Left<TFromType> lft: | |
return new Left<TToType>(lft.Exception); | |
default: | |
return new Left<TToType>(new Exception("Default value")); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment