Rui Carvalho
Software craftsman, experienced in Microsoft technologies
Student Talks
we will talk about:
01
why
WHY ???
$> OOP has been the de facto standard these last 20 years and a second nature for most developers $> rm -rf ~/
01
why
01
why
Science
Bank
Artificial Intelligence
01
why
01
why
1/ well, there is 2 kinds of OOP ...
01
why
1/ well, there is 2 kinds of OOP ...
the basic idea of OOP is that objects encapsulate their concerns
but today, most OO programs are built with anemic models ...
01
why
2/ Hell, do you know that object based programming is not the same as object oriented ?
Most apps I see are about procedural code within objects not OOP (polymorphism, inheritence, etc ...)
01
why
01
why
public class MyProceduralApp
{
public double Execute(params string[] args)
{
if(args.Length < 3) throw new Exception("bad arguments");
var command = args[0];
var arguments = args.Skip(1);
double result = 0;
if(command == "add")
{
double a,b;
if(double.TryParse(args[1],out a)
&& double.TryParse(args[2], out b))
{
result = a+b;
} else {
throw new Exception("args 1 & 2 are not numbers");
}
}
else if (command == "sub")
{
double a,b;
if(double.TryParse(args[1],out a)
&& double.TryParse(args[2], out b))
{
result = a-b;
} else {
throw new Exception("args 1 & 2 are not numbers");
}
}
else if (command == "prod")
{
double a,b;
if(double.TryParse(args[1],out a)
&& double.TryParse(args[2], out b))
{
result = a*b;
} else {
throw new Exception("args 1 & 2 are not numbers");
}
}
return result;
}
}public class MyBetterOOpApp {
public double
ExecuteOperation (string operationName, double a, double b)
=> new Calculator().Execute(operationName,a,b);
}
public class Calculator {
public IOperation GetOperatorFromName(string name) {
switch (name)
{
case "add": return new AddOperation();
case "sub": return new SubOperation();
case "prod": return new ProdOperation();
default: throw new Exception("bad operation name");
}
}
public double Execute(string op, double left, double right)
=> GetOperatorFromName(op).Execute(left,right);
}
public interface IOperation {
double Execute(double left, double right);
}
public class AddOperation : IOperation {
public double Execute (double left, double right)
=> left + right;
}
public class SubOperation : IOperation {
public double Execute (double left, double right)
=> left - right;
}
public class ProdOperation : IOperation {
public double Execute (double left, double right)
=> left * right;
}var result1 = new MyProceduralApp().Execute("add","1","2");
Console.WriteLine($"result 1+2= {result1}");
var result2 = new MyBetterOOpApp().ExecuteOperation("add",1,2);
Console.WriteLine($"result 1+2= {result2}");you should have a deeper look into functional style even if you're doing procedural or object oriented programming
01
why
01
why
~1970 -> ~2005
2 years
2 years
2 years
~2005 -> ???
2 years
2 years
2 years
01
why
01
why
01
why
(mostly from shared states)
01
why
there is in all the cases limits to parallelism
01
why
Speedup in latency of the execution of a program in function of the number of processors is limited by the serial part of the program
you should have a deeper look into functional style even if you're doing procedural or object oriented programming
01
01
why
01
why
01
why
object Apple of abstract Fruit
Smoothie Factory
BlenderEngine for abstract Fruits
Generates
ProduceJuice method of abstract Juice
Apple
Blend Function
input
object AppleJuice of abstract Juice
AppleJuice
output
01
why
object Apple of abstract Fruit
Smoothie Factory
BlenderEngine for abstract Fruits
Generates
ProduceJuice method of abstract Juice
object AppleJuice of abstract Juice
let's do a cocktail now !
Alcool Builder
Build From AlcoolType
Rum
Cocktail Factory
generates
Apple Mojito builder
uses
uses
Build
Apple Mojito
01
why
let's do a cocktail now !
Apple
Blend Function
input
AppleJuice
output
Rum
Shaker Function
input
input
output
Apple Mojito
... and yes, I know, i'm judging and not totally partial
you should have a deeper look into functional style even if you're doing procedural or object oriented programming
01
01
why
after playing with a repl, you’ll always miss one!
01
why
01
why
02
... and a different way of thinking about programs than the mainstream, imperative, mostly object oriented paradigms you're probably used to
02
ideas
02
ideas
02
ideas
02
ideas
Functions are top level constructs like classes in object oriented languages
Functions are usually grouped into modules
module Calculator
function Add = (x,y) -> x + y public class Calculator {
public static long Add (int x, int y) {
return x+y
}
}02
ideas
there is 2 flavors of functional programming : the ones that are dynamically typed and the ones that provide strong typing
let message = ("hello","rui")type word = string
type message = list of words
let greeting = ["hello", "word"]dynamic -> better flexibility
great types -> better models, less errors
02
ideas
that's the general idea, we have functions everywhere and the output of one is the input of the next
let buildGreeting name = $"hello {name}, how are you?"
let splitWords sentence = String.split (sentence, " ")
let capitalizeFirstLetter list = Seq.map (
word -> head(word).ToUpper() + tail(word) )
let buildFinalSentence list = Seq.join (list, " ")
let greetingsFor name =
buildGreeting
|> splitWords
|> capitalizeFirstLetter
|> buildFinalSentence
let message = greetingsFor "rui"
//print : Hello Rui, How Are You?"
02
ideas
loops, storage
rather than statements
for better models
for control flow
to avoid side effects
for safety
02
ideas
rather than statements
like SQL
int result;
if( condition) {
result = 42;
} else {
result = -1;
}statements style
int result =
condition ? 42 : -1 ;expression style
02
ideas
for better models
new type = type1 AND type2
type BasketItem = {
name: string;
quantity: int}
let aBook = {
name="Dune";
quantity=3}
new type = type1 OR type2
type Card =
| Head of string
| Number of int
let card1 = Head "King"
let card2 = Number 1
02
ideas
for safety
Immutable data makes the code predictable
Immutable data is easier to work with
Immutable data forces you to use a “transformational” approach
02
ideas
for control flow
type Shape =
| Circle of radius:int
| Rectangle of height:int * width:int
let surface shape =
match shape with
| Circle r -> r * r * 3.14
| Rectangle (x,y) -> x * y02
ideas
to avoid side effects
// pure
let add x y = x + y
// not pure
let mutable counter = 0
let inc = counter + 1
02
ideas
to avoid side effects
but with immutability and pure functions, how could I change data or do a for loop?
for safety
02
ideas
loops, storage
let rec do5 action index =
action()
if index < 5
return do5 action index+1
else
return "done!"(let's try to write real code from these ideas )
03
03
take
=> Use Delegates to define your contracts
=> Use Lambdas to define your implementations
03
take
Use Delegates to define your contracts
Use Lambdas to define your implementations
public interface IOperation {
double Execute(long left, long right);
}
public class AddOperation : IOperation {
public double Execute(long left, long right) {
return left + right;
}
}
public class Calculator
{
public double Execute(
IOperation operation, long left, long right)
=> operation.Execute(left,right);
}
public class App
{
public void Main() {
var calculator = new Calculator();
IOperation operation = new AddOperation();
var result = calculator.Execute(operation,1,2);
Console.Write($"1+2={result}");
}
}public delegate double
Operation(long left, long right);
public class Calculator {
public static double Execute(
Operation operation, long left, long right)
=> operation(left,right);
}
public class App {
public void Main() {
var result = Calculator.Execute(
(x,y) => x+y, 1, 2);
Console.Write($"1+2={result}");
}
}from this to this
03
take
Text
if your functions have no state and no side effects it's ok to use static
public static class StringUtils {
public static string CapitalizeFirst(string word)
=> word.Substring(0,1).ToUpper()
+ word.Substring(1).ToLower();
public static string Line(int size, char element = '-')
=> new String(element,size);
}03
take
it's even better if you can "import" the static class in your language
public static class StringUtils {
public static string CapitalizeFirst(string word)
=> word.Substring(0,1).ToUpper()
+ word.Substring(1).ToLower();
public static string Line(int size, char element = '-')
=> new String(element,size);
}using static System.Console;
using static GlobalFunctions.StringUtils;
public class RunDemo
{
public static void Run()
{
WriteLine("Hello" + CapitalizeFirst("rui"));
WriteLine(Line(80));
}
}03
take
a higher-order function (also functional, functional form or functor) is a function that does at least one of the following:
think in terms of (reusable) expressions instead of statements
simplifies decoupling
03
take
public static Func<string> GetIdGenerator()
{
return ()=> Guid.NewGuid()
.ToString().Substring(0,8);
}
public static void PrintResult<T>(Func<T> action)
{
T result = action();
Console.WriteLine($"result = {result}");
}Func<string> generateNewId = GetIdGenerator();
PrintResult(generateNewId);
PrintResult(generateNewId);
//result = ec1ed257
//result = ce36e7f7your lib
usage
03
take
a must have!
function declaration can sometimes be messy, take advantage of type inference!
Func<Tuple<int,int>,string,string> listToString1 =
(Tuple<int,int> t, string message)
=> $"({message}{t.Item1},{t.Item2})";don't work, you need to write this:
//because "Cannot assign lambda function to an implicit-typed variable"
var listToString1 =
(Tuple<int,int> t, string message)
=> $"({message}{t.Item1},{t.Item2})";03
take
a must have!
with this small helper:
Func<Tuple<int,int>,string,string> listToString1 =
(Tuple<int,int> t, string message)
=> $"({message}{t.Item1},{t.Item2})";can be written like this :
public static Func<T1,T2>
fun<T1,T2>(Func<T1,T2> function) => function;var tupleToString = fun(
(Tuple<int,int> t, string message) => $"({t.Item1},{t.Item2})");03
take
while procedural/objects are about statements, functional is about expressions and composition of expressions
var dico = new Dictionary<int,string> {
[1]="one",[2]="two",[3]="three"
};
var list = new List<string>();
foreach(var item in dico)
{
list.Add(item.Value);
}
var result = string.Empty;
foreach(var item in list)
{
result += item + ",";
}
result = result.Substring(0,result.Length-1);ex: take this procedural style code
03
take
while procedural/objects are about statements, functional is about expressions and composition of expressions
public static Dictionary<int,string>
GetDico() => new Dictionary<int,string> {
[1]="one",[2]="two",[3]="three"
};
public static IEnumerable<U> DicoToList<T,U>
(Dictionary<T,U> dico) => dico.Values;
public static string ConcatList
(IEnumerable<string> list) => String.Join(",",list);if you refactor it to functions
var dico = GetDico();
var list = DicoToList(dico);
var concatList1 = ConcatList(list);
WriteLine(concatList1);you'll use it like this
03
take
we would like to chain these functions to create a composition, but no pipes in OOP ;-(
//not working:
let concatList =
GetDico()
|> DicoToList
|> ConcatList
we would like to write it like this
var concatList =
ConcatList(
DicoToList(
GetDico()));but we can only compose like this:
03
take
solution : another Helper!
public static U Then<T,U>
(this T current, Func<T,U> transform)
=> transform(current);and now we can write the full expression
//v3 = functional style : combine expressions:
WriteLine(
GetDico()
.Then(DicoToList)
.Then(ConcatList));03
take
at every statement step you must verify the result or catch the error, this will be quickly messy!
double Div(double a, double b) {
if (b==0) throw new DivideByZeroException();
return a/b;
}
string AddLabel(double number) {
return $"value = {number.Value}";
}
try {
var result = Div(1,0);
var label = AddLabel(result);
WriteLine(label);
} catch(Exception ex)
{
WriteLine("error : " + ex.Message)
}
03
take
why not manage errors all along transparentrly and unwrap thing only at the end?
Result<double> Div(double a, double b) {
if (b==0) return new Failure<double>("divide by zero");
return new Success(a/b);
}
Result<string> AddLabel(Result<double> number) {
if(! number.IsSuccess) return new Failure<string>(number.Message);
return $"value = {number.Value}";
}
Div(1,0)
.Then(AddLabel)
.Then(
success: label => WriteLine(label),
failure: err => WriteLine("error : " + err.Message)03
take
void are black holes where you'll be trapped, you don't know what can happened!
03
take
what is the percentage of your code where you are checking for the non-nullity of a variable ???
at least, use a default None value for your type
03
take
if a variable can't change, you couldn't have side effects!
because classes are reference types, think about using more enums & structs!
03
take
1. think in functions
2. use pure static global functions
3. use High Order Functions
4. use Helpers to facilitate functions
5. compose workflow with functions
6. handle errors with railway programming
7. avoid void, always return something!
7. avoid nulls
8. immutability
http://github.com/rhwy/beingfunctional
2017, 18-19 of May
components ?
Response = f(request)
Request -> Async<Response>
#r "packages/Suave/lib/net40/Suave.dll"
open Suave
startWebServer defaultConfig
(Successful.OK "Hello World!")By Rui Carvalho
A little introduction to functional programming concepts for the procedural object oriented programmer with some concrete exemples on what you can use in your day to day code