Upload
tdc-globalcode
View
200
Download
1
Embed Size (px)
Citation preview
C# como você nunca viu! Conceitos avançados de
programação functional em .NETElemar Júnior
@[email protected]@gmail.com
elemarjr.com
Olá, eu sou Elemar Jr
Programação functional com C#
Mas, vamos falar de
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
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
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); }}
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); }}
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); }}
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); }}
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;}
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;}
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;}
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;}
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
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);}
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(); } }}
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(); }); }}
public class Program{ public static void Main() { using (var reader = new StreamReader("file.txt")) { WriteLine(reader.ReadToEnd()); } }}
public class Program{ public static void Main() { string content; using (var reader = new StreamReader("file.txt")) { content = reader.ReadToEnd(); } //.. }}
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); }}
public static TR Using<T, TR>( T input, Func<T, TR> func ) where T : IDisposable{ using (input) return func(input);}
public class Program{ public static void Main() { var content = Using(new StreamReader("file.txt"), reader => reader.ReadToEnd() ); }}
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
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); }}
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));
public interface IRepository<T, TId>{ T GetById(TId id);}
O problema com o null
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 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 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..." );
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;}
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..." );}
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")}
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}");
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);
var host = AppSettings["RavenDB.Host"] ?? "localhost";
int port;if (!int.TryParse(AppSettings["RavenDB.Port"], out port)) port = 8080;
// ..
public static class Parsing{ public static Option<int> ParseInt(this string s) { int result; return int.TryParse(s, out result) ? Some(result) : None; }}
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);
var host = AppSettings["RavenDB.Host"] ?? "localhost";var port = AppSettings["RavenDB.Port"].ParseInt().GetOrElse(8080);
public static Option<T> Where<T>( this Option<T> @this, Func<T, bool> predicate ) => @this.IsSome && predicate(@this.Value) ? @this : None;
var host = AppSettings["RavenDB.Host"] ?? "localhost";var port = AppSettings["RavenDB.Port"].ParseInt() .Where(p => p > 0) .GetOrElse(8080);
public interface IRepository<T, TId>{ Option<T> GetById(TId id);}
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);}
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!");}
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!");}
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; }; }}
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!");}
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; };}
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!");}
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
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);}
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;}
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));
WriteLine(Divide(a, b).Match( right: res => $"Result {res}", left: error => $"Error {error}") );
C# como você nunca viu! Conceitos avançados de
programação functional em .NETElemar Júnior
@[email protected]@gmail.com
elemarjr.com