65
Hello! I am Mikhail Shilkov I am here to talk some Functional Programming and F# You can nd me at @MikhailShilkov and http://mikhail.io

Introduction of Functional Programming

Embed Size (px)

Citation preview

Hello!I am Mikhail Shilkov

I am here to talk some Functional Programming and F#

You can �nd me at @MikhailShilkov and http://mikhail.io

Poll

AgendaLearn Functional Programming - 45 min

Learn F# - 30 min

FunctionalProgramming

Here's how it works

PrinciplesConstraints

around yourcode

Bene잷�tsUseful

properties thathold under

theseconstraints

AdoptionSee where

these bene�tsmake thedi�erence

PrinciplesHow is functional programming di�erent?

Purity

Function as mapping

Domain Codomain

Determinism

// This is not deterministic public bool IsMoreThanHourAgo(DateTime time) {            var hour = TimeSpan.FromHours(‐1);   return time < DateTime.Now.Add(hour); } 

Determinism

// This is deterministic public bool IsMoreThanHourApart(DateTime time, DateTime anotherTime) {            var hour = TimeSpan.FromHours(‐1);   return time < anotherTime.Add(hour); } 

No side effects

public class TextCounter {   private int wordCount = 0;   private int charCount = 0;    public void Count(string word)   {     this.WordCount += 1;     this.CharCount += word.Length;   }    public void Print() =>     Console.WriteLine($"Words: {wordCount}, characters: {charCount}"); }

No side effects

public class TextCounter {   public Tuple<int, int> Count(string[] word)   {     var wordCount = word.Length;     var charCount = word.Sum(w => w.Length);     return Tuple.Create(wordCount, charCount);   } }

Immutability

var importantThing = /*...*/; var result = DoWork(importantThing); // Can I trust my importantThing is unchanged?

Totality

One more function

Int Int

What's wrong here?

Int Int

public static int sqrtPlus1(int x) {    if (x < 0) throw new ArgumentException(nameof(x));    return Math.Floor(Math.Sqrt(x)) + 1; } 

Natural Int

Int Complex

No Exceptions for control flow

public static int sqrtPlus1(int x) {    if (x < 0) throw new ArgumentException(nameof(x));    return Math.Floor(Math.Sqrt(x)) + 1; } 

Strong typesystem

Everything is a type

Primitive types

Int String

Algebraic Data Types

Int  * Int Byte * Boolean               Int  | String Byte | Boolean

Function Types

Int ‐> Int Int[] ‐> String Int ‐> Int ‐> Int (Int ‐> Int ‐> Int) ‐> Int[] ‐> Int

Generics

T ‐> U `a[] ‐> `b

Side effects

unit String ‐> unit IO[T]

No NULLs

public interface IGetThing {     // Can the result be null?     Thing Get(int id); } 

No NULLs

public interface IGetThing {     // Hey, result is optional     Option<Thing> Get(int id); } 

Making invalid statesunrepresentable

public class Contact  {   public string Name { get; set; }   public EmailInfo Email { get; set; }   public AddressInfo Postal { get; set; } }

Making invalid statesunrepresentable

type ContactInfo =    | EmailOnly of EmailInfo   | PostOnly of AddressInfo   | EmailAndPost of EmailInfo * AddressInfo  type Contact = {   Name: string;   Contact: ContactInfo; }

BenefitsWhy do we want functional programming?

Formal reasoning

Referencial transparency

sqrt(mult2(add3(5)) sqrt(mult2(8)) sqrt(16) 4 

Control Flow Expression Evaluation

Type signature as documentation

int ‐> int Customer ‐> Address U ‐> T[] ‐> U Identifier ‐> Task<Customer>

Composition

Succinct, concise and precise code

Higher Order Functions

var categories = products     .GroupBy(prod => prod.Category)     .Select(prodGroup => new {        prodGroup,          minPrice = prodGroup.Min(p => p.UnitPrice)     })     .Select(t => new {         Category = t.prodGroup.Key,          CheapestProducts = t.prodGroup             .Where(p => p.UnitPrice == t.minPrice)     });

Recursion

qsort [] = []  qsort [a] = [a]  qsort (a:as) = let (lesser, greater) = partition a as                in qsort lesser ++ [a] ++ qsort greater

Parallelism

Testabilty

Pure function is the easiest thing totest

Functions are intrinsically mockable

Parameterized testing

[Test] public void MyTestCase() {     var a = 5;     var b = 10;          var result = target.Add(a, b); 

    result.Should().Be(15); } 

Parameterized testing

[TestCase(5, 10, 15)] [TestCase(2, 3, 5)] [TestCase(‐2, 2, 0)] public void MyTestCase(int a, int b, int c) {     var result = target.Add(a, b); 

    result.Should().Be(c); } 

Property-based testing

[Property] public void AddToZeroDoesNotChangeTheNumber(int a) {     var result = target.Add(a, 0);     result.Should().Be(a); }  [Property] public void OrderDoesNotMatter(int a, int b) {     var result1 = target.Add(a, b);     var result2 = target.Add(b, a);     result1.Should().Be(result2); }

AdoptionDo people use FP in modern applications?

Your code should better beSOLID!

public class FileStore : IMessageQuery {   private readonly DirectoryInfo workingDirectory;      public FileStore(DirectoryInfo workingDirectory)   {              this.workingDirectory = workingDirectory;   }      

  public string Read(int id)   {              var path = Path.Combine(       this.workingDirectory.FullName,        id + ".txt");     return File.ReadAllText(path);   }  } 

Domain Driven Design

Event Sourcing

Event   E

data

Projection   P

data

Event handler 

E[] -> Pfunction

Event  E

data

Command  C

data

Commandhandler

 C -> E[]function

Actor Frameworks

Akka Streams

val counterRunnableGraph: RunnableGraph[Future[Int]] =   tweetsInMinuteFromNow     .filter(_.hashtags contains akkaTag)     .map(t => 1)     .toMat(sumSink)(Keep.right)

Apache Hadoop / MapReduce

Apache Hadoop / MapReduce

// This class performs the map operation, translating raw input into the key‐value // pairs we will feed into our reduce operation. class TokenizerMapper extends Mapper[Object,Text,Text,IntWritable] {   val one = new IntWritable(1)   val word = new Text      override   def map(key:Object, value:Text, context:Mapper[Object,Text,Text,IntWritable]#Context) = {     for (t <‐  value.toString().split("\\s")) {       word.set(t)       context.write(word, one)     }   } }    // This class performs the reduce operation, iterating over the key‐value pairs // produced by our map operation to produce a result. In this case we just // calculate a simple total for each word seen. class IntSumReducer extends Reducer[Text,IntWritable,Text,IntWritable] {   override   def reduce(key:Text, values:java.lang.Iterable[IntWritable], context:Reducer[Text,IntWritable,Text,IntWritable]#Context) = {     val sum = values.foldLeft(0) { (t,i) => t + i.get }     context.write(key, new IntWritable(sum))   } }    // This class configures and runs the job with the map and reduce classes we've // specified above. object WordCount { 

  def main(args:Array[String]):Int = {     val job = new Job(conf, "word count")     job.setJarByClass(classOf[TokenizerMapper])     job.setMapperClass(classOf[TokenizerMapper])     job.setCombinerClass(classOf[IntSumReducer])     job.setReducerClass(classOf[IntSumReducer])     job.setOutputKeyClass(classOf[Text])     job.setOutputValueClass(classOf[IntWritable])     FileInputFormat.addInputPath(job, new Path(args(0)))     FileOutputFormat.setOutputPath(job, new Path((args(1))))     if (job.waitForCompletion(true)) 0 else 1   } 

}

Apache Spark

Apache Spark

val sc = new SparkContext(conf) val input =  sc.textFile(inputFile) val words = input.flatMap(line => line.split(" ")) val counts = words.map(word => (word, 1))                   .reduceByKey{case (x, y) => x + y} counts.saveAsTextFile(outputFile)

Apache Kafka

Wrapping Up

Functional Programming is...

Thanks!Mikhail Shilkov

Next: Introduction of F#

You can �nd me at @MikhailShilkov and http://mikhail.io