The document discusses concepts of functional programming in C# and .NET, including:
- Using functions as first-class citizens and higher-order functions like Map and Where
- Directing code towards immutability using concepts like Option and Either to represent failure
- Handling concurrency issues through immutable and referentially transparent functions
- Combining functions through combinators like Print and Time to add logging and profiling
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de programação funcional em .NET
1. C# como você nunca viu!
Conceitos avançados de
programação functional em .NET
Elemar Júnior
@elemarjr
elemarjr@ravendb.net
elemarjr@gmail.com
elemarjr.com
4. Func<int, int> square = x => x*x;
var range = Enumerable.Range(1, 10);
var sqs = range.Select(square);
Funções são 1st class citzens
5. Func<int, bool> isEven = x => x%2 == 0;
var original = new[] {4, 7, 9, 3, 2};
var sorted = original.OrderBy(x => x);
var filtered = original.Where(isEven);
Direcionamento a imutabilidade
6. using System;
using System.Threading.Tasks;
using static System.Linq.Enumerable;
using static System.Console;
class Program
{
static void Main()
{
Title = "Concurrent problems";
var data = Range(-10000, 20001).Reverse().ToList();
// [10000, 9999, .. -9999, -10000]
Action t1 = () => WriteLine($"T1 = {data.Sum()}");
Action t2 = () =>
{
data.Sort();
WriteLine($"T2 = {data.Sum()}");
};
Parallel.Invoke(t1, t2);
}
}
7. using System;
using System.Threading.Tasks;
using static System.Linq.Enumerable;
using static System.Console;
class Program
{
static void Main()
{
Title = "Concurrent problems";
var data = Range(-10000, 20001).Reverse().ToList();
// [10000, 9999, .. -9999, -10000]
Action t1 = () => WriteLine($"T1 = {data.Sum()}");
Action t2 = () =>
{
data.Sort();
WriteLine($"T2 = {data.Sum()}");
};
Parallel.Invoke(t1, t2);
}
}
8. using System;
using System.Threading.Tasks;
using static System.Linq.Enumerable;
using static System.Console;
class Program
{
static void Main()
{
Title = "Concurrent problems";
var data = Range(-10000, 20001).Reverse().ToList();
// [10000, 9999, .. -9999, -10000]
Action t1 = () => WriteLine($"T1 = {data.Sum()}");
Action t2 = () =>
{
data.Sort();
WriteLine($"T2 = {data.Sum()}");
};
Parallel.Invoke(t1, t2);
}
}
9. using System;
using System.Threading.Tasks;
using static System.Linq.Enumerable;
using static System.Console;
class Program
{
static void Main()
{
Title = "Concurrent problems";
var data = Range(-10000, 20001).Reverse().ToList();
// [100, 99, .. -99, -100]
Action t1 = () => WriteLine($"T1 = {data.Sum()}");
Action t2 = () => WriteLine($"T2 = {data.OrderBy(x => x).Sum()}");
Parallel.Invoke(t1, t2);
}
}
10. class Rectangle
{
public double SideA { get; set; }
public double SideB { get; set; }
public Rectangle(double sideA, double sideB)
{
SideA = sideA;
SideB = sideB;
}
public double Area => SideA*SideB;
}
11. class Rectangle
{
public double SideA { get; set; }
public double SideB { get; set; }
public Rectangle(double sideA, double sideB)
{
SideA = sideA;
SideB = sideB;
}
public double Area => SideA*SideB;
}
12. struct class Rectangle
{
public double SideA { get; }
public double SideB { get; }
public Rectangle(double sideA, double sideB)
{
SideA = sideA;
SideB = sideB;
}
public double Area => SideA*SideB;
}
13. struct Rectangle
{
public double SideA { get; }
public double SideB { get; }
public Rectangle(double sideA, double sideB)
{
SideA = sideA;
SideB = sideB;
}
public Rectangle WithSideA(double newSideA) =>
new Rectangle(newSideA, SideB);
public Rectangle WithSideB(double newSideB) =>
new Rectangle(SideB, newSideB);
public double Area => SideA*SideB;
}
14. static IEnumerable<T> Where<T>(
this IEnumerable<T> source,
Func<T, bool> predicate
)
{
foreach (var e in source)
{
if (predicate(e))
yield return e;
}
}
Você já conhece High-order functions
15. using System;
using static System.Diagnostics.Debug;
class Program
{
static void Main()
{
Func<int, int, int> divide =
(a, b) => a/b;
var divideBy = divide.SwapArgs();
Assert(divide(8, 2) == divideBy(2, 8));
}
}
static class Extensions
{
public static Func<T2, T1, TResult> SwapArgs<T1, T2, TResult>(
this Func<T1, T2, TResult> f
) => (t2, t1) => f(t1, t2);
}
16. using System;
using static System.Console;
class Resource : IDisposable
{
public Resource() { WriteLine("created ..."); }
public void Foo() => WriteLine("Foo");
public void Fee() => WriteLine("Fee");
public void Dispose() => WriteLine("cleanup...");
}
public class Program
{
public static void Main()
{
using (var r = new Resource())
{
r.Foo();
r.Fee();
}
}
}
17. using System;
using static System.Console;
class Resource //: IDisposable
{
private Resource() { WriteLine("created ..."); }
public void Foo() => WriteLine("Foo");
public void Fee() => WriteLine("Fee");
void Dispose() => WriteLine("cleanup...");
public static void Use(Action<Resource> block)
{
var r = new Resource(); // pooling?
try { block(r); } finally { r.Dispose(); }
}
}
public class Program
{
public static void Main()
{
Resource.Use(r => {
r.Foo();
r.Fee();
});
}
}
18. public class Program
{
public static void Main()
{
using (var reader = new StreamReader("file.txt"))
{
WriteLine(reader.ReadToEnd());
}
}
}
19. public class Program
{
public static void Main()
{
string content;
using (var reader = new StreamReader("file.txt"))
{
content = reader.ReadToEnd();
}
//..
}
}
20. using System;
using System.IO;
using static Functional;
using static System.Console;
public class Program
{
public static void Main()
{
Using(new StreamReader("file.txt"), reader => {
WriteLine(reader.ReadToEnd());
});
}
}
public static class Functional
{
public static void Using<T>(
T input,
Action<T> action
) where T : IDisposable
{
using (input) action(input);
}
}
21. public static TR Using<T, TR>(
T input,
Func<T, TR> func
) where T : IDisposable
{
using (input) return func(input);
}
22. public class Program
{
public static void Main()
{
var content = Using(new StreamReader("file.txt"), reader =>
reader.ReadToEnd()
);
}
}
23. public static class Functional
{
public static void Using<T>(
T input,
Action<T> action
) where T : IDisposable
{
using (input) action(input);
}
public static TR Using<T, TR>(
T input,
Func<T, TR> func
) where T : IDisposable
{
using (input) return func(input);
}
}
O problema com o Void
24. public class Program
{
public static void Main()
{
Using(new StreamReader("file.txt"), reader =>
{
WriteLine(reader.ReadToEnd());
return Unit();
});
}
}
public struct Unit { }
public static class Functional
{
static readonly Unit unit = new Unit();
public static Unit Unit() => unit;
public static TR Using<T, TR>(
T input,
Func<T, TR> func
) where T : IDisposable
{
using (input) return func(input);
}
}
25. public static Func<T, Unit> ToFunc<T>(Action<T> action) => o =>
{
action(o);
return Unit();
};
Action<StreamReader> print = (s) => WriteLine(s.ReadToEnd());
Using(new StreamReader("file.txt"), ToFunc(print));
27. public struct Option<T>
{
internal T Value { get; }
public bool IsSome { get; }
public bool IsNone => !IsSome;
internal Option(T value, bool isSome)
{
IsSome = isSome;
Value = value;
}
public TR Match<TR>(Func<T, TR> some, Func<TR> none)
=> IsSome ? some(Value) : none();
}
public static class Option
{
public static Option<T> Of<T>(T value)
=> new Option<T>(value, value != null);
}
28. public struct Option<T>
{
internal T Value { get; }
public bool IsSome { get; }
public bool IsNone => !IsSome;
internal Option(T value, bool isSome)
{
IsSome = isSome;
Value = value;
}
public TR Match<TR>(Func<T, TR> some, Func<TR> none)
=> IsSome ? some(Value) : none();
}
public static class Option
{
public static Option<T> Of<T>(T value)
=> new Option<T>(value, value != null);
}
public static void Main()
{
string _ = null, s = "Elemar";
var o1 = Option.Of(_); // None
var o2 = Option.Of(s); // Some("Elemar")
}
29. public struct Option<T>
{
internal T Value { get; }
public bool IsSome { get; }
public bool IsNone => !IsSome;
internal Option(T value, bool isSome)
{
IsSome = isSome;
Value = value;
}
public TR Match<TR>(Func<T, TR> some, Func<TR> none)
=> IsSome ? some(Value) : none();
}
public static class Option
{
public static Option<T> Of<T>(T value)
=> new Option<T>(value, value != null);
}
public static void Main()
{
string _ = null, s = "Elemar";
var o1 = Option.Of(_); // None
var o2 = Option.Of(s); // Some("Elemar")
}
public void SayHello(string name)
=> WriteLine(GreetingFor(name));
public string GreetingFor(string name)
=> Option.Of(name).Match(
some: n => $"Hello {n}",
none: () => "Sorry..."
);
30. public struct NoneType { }
public static class Functional
{
public static Option<T> Some<T>(T value) => Option.Of(value);
public static readonly NoneType None = new NoneType();
…
public struct Option<T>
{
...
public static readonly Option<T> None = new Option<T>();
public static implicit operator Option<T>(T value)
=> Some(value);
public static implicit operator Option<T>(NoneType _)
=> None;
}
31. public class Program
{
public static void Main()
{
SayHello("Elemar");
SayHello(null);
}
public static void SayHello(string name)
=> WriteLine(GreetingFor(name));
public static string GreetingFor(Option<string> name)
=> name.Match(
some: n => $"Hello {n}",
none: () => "Sorry..."
);
}
32. public static class Option
{
public static Option<T> Of<T>(T value)
=> new Option<T>(value, value != null);
public static Option<TR> Map<T, TR>(
this Option<T> @this,
Func<T, TR> func) =>
@this.IsSome
? Some(func(@this.Value))
: None;
}
public static void Main()
{
string _ = null, s = "Elemar";
Func<string, string> greetingFor = name => $"Hello {name}";
var o1 = Option.Of(_).Map(greetingFor); // None
var o2 = Option.Of(s).Map(greetingFor); // Some("Hello Elemar")
}
33. public static class Option
{
public static Option<T> Of<T>(T value)
=> new Option<T>(value, value != null);
public static Option<TR> Map<T, TR>(
this Option<T> @this,
Func<T, TR> func) =>
@this.IsSome
? Some(func(@this.Value))
: None;
}
Option<string> _ = null, s = "Elemar";
_.Map(n => $"Hello {n}");
s.Map(n => $"Hello {n}");
34. public static Option<Unit> ForEach<T>(
this Option<T> @this,
Action<T> action) =>
Map(@this, ToFunc(action));
Option<string> _ = null, s = "Elemar";
_.Map(n => $"Hello {n}").ForEach(WriteLine);
s.Map(n => $"Hello {n}").ForEach(WriteLine);
35. var host = AppSettings["RavenDB.Host"] ?? "localhost";
int port;
if (!int.TryParse(AppSettings["RavenDB.Port"], out port))
port = 8080;
// ..
36. public static class Parsing
{
public static Option<int> ParseInt(this string s)
{
int result;
return int.TryParse(s, out result)
? Some(result)
: None;
}
}
37. public static T GetOrElse<T>(
this Option<T> @this,
Func<T> fallback) =>
@this.Match(
some: value => value,
none: fallback
);
public static T GetOrElse<T>(
this Option<T> @this,
T @else) =>
GetOrElse(@this, () => @else);
38. var host = AppSettings["RavenDB.Host"] ?? "localhost";
var port = AppSettings["RavenDB.Port"].ParseInt().GetOrElse(8080);
39. public static Option<T> Where<T>(
this Option<T> @this,
Func<T, bool> predicate
)
=> @this.IsSome && predicate(@this.Value) ? @this : None;
40. var host = AppSettings["RavenDB.Host"] ?? "localhost";
var port = AppSettings["RavenDB.Port"].ParseInt()
.Where(p => p > 0)
.GetOrElse(8080);
42. public void Run()
{
WriteLine("Starting...");
var f30 = Fibonacci(30);
var f35 = Fibonacci(35);
var f40 = Fibonacci(40);
var f45 = Fibonacci(45);
WriteLine("Results: {0}, {1}, {2}, {3}", f30, f35, f40, f45);
WriteLine("Done!");
}
public int Fibonacci(int n)
{
if (n == 0) return 0;
if (n == 1) return 1;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
43. public void Run2()
{
Func<int, int> fibonacci = Fibonacci;
WriteLine("Starting...");
var f30 = fibonacci(30);
var f35 = fibonacci(35);
var f40 = fibonacci(40);
var f45 = fibonacci(45);
WriteLine($"Results: {f30}, {f35}, {f40}, {f45}");
WriteLine("Done!");
}
44. public void Run3()
{
Func<int, int> fibonacci = Fibonacci;
WriteLine("Starting...");
var f30 = fibonacci(30);
WriteLine($"Input: 30 Result: {f30}");
var f35 = fibonacci(35);
WriteLine($"Input: 35 Result: {f35}");
var f40 = fibonacci(40);
WriteLine($"Input: 40 Result: {f40}");
var f45 = fibonacci(45);
WriteLine($"Input: 50 Result: {f45}");
WriteLine($"Results: {f30}, {f35}, {f40}, {f45}");
WriteLine("Done!");
}
45. public static class Combinators
{
public static Func<T, TResult> Print<T, TResult>(Func<T, TResult> func)
{
return (input) =>
{
var result = func(input);
WriteLine($"Input: {input} Result: {result}");
return result;
};
}
}
46. public void Run4()
{
var fibonacci = Combinators.Print<int, int>(Fibonacci);
WriteLine("Starting...");
var f30 = fibonacci(30);
var f35 = fibonacci(35);
var f40 = fibonacci(40);
var f45 = fibonacci(45);
WriteLine($"Results: {f30}, {f35}, {f40}, {f45}");
WriteLine("Done!");
}
47. public static Func<T, TResult> Time<T, TResult>(Func<T, TResult> func)
{
return (input) =>
{
var before = DateTime.Now;
var result = func(input);
WriteLine($"Time to this input: {DateTime.Now - before}");
return result;
};
}
48. public void Run5()
{
var fibonacci = Combinators.Time(
Combinators.Print<int, int>(Fibonacci)
);
WriteLine("Starting...");
var f30 = fibonacci(30);
var f35 = fibonacci(35);
var f40 = fibonacci(40);
var f45 = fibonacci(45);
WriteLine($"Results: {f30}, {f35}, {f40}, {f45}");
WriteLine("Done!");
}
49. public static double Divide(double a, double b)
{
if (Math.Abs(b) < 0.00001)
{
throw new ArgumentException("b could not be 0.");
}
return a/b;
}
O problema com o Exceptions
50. public struct Either<TL, TR>
{
internal TL Left { get; }
internal TR Right { get; }
public bool IsLeft => !IsRight;
public bool IsRight { get; }
internal Either(TL left)
{
IsRight = false;
Left = left;
Right = default(TR);
}
internal Either(TR right)
{
IsRight = true;
Right = right;
Left = default(TL);
}
public static implicit operator Either<TL, TR>(TL left)
=> new Either<TL, TR>(left);
public static implicit operator Either<TL, TR>(TR right)
=> new Either<TL, TR>(right);
}
51. public static Either<string, double> Divide(double a, double b)
{
if (Math.Abs(b) < 0.00001)
{
return "b could not be 0.";
}
return a/b;
}
52. public TR Match<TR>(Func<L, TR> left, Func<R, TR> right)
=> IsLeft ? left(Left) : right(Right);
public Unit Match<TR>(Action<L> left, Action<R> right)
=> Match(ToFunc(left), ToFunc(right));
55. C# como você nunca viu!
Conceitos avançados de
programação functional em .NET
Elemar Júnior
@elemarjr
elemarjr@ravendb.net
elemarjr@gmail.com
elemarjr.com