Skip to content

Instantly share code, notes, and snippets.

@eramax
Last active September 26, 2021 05:49
Show Gist options
  • Save eramax/e90890d93f8af6779f0dc2bebbecdac2 to your computer and use it in GitHub Desktop.
Save eramax/e90890d93f8af6779f0dc2bebbecdac2 to your computer and use it in GitHub Desktop.
Functional Programming with C# - Simon Painter
/// <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