Upload
mikhail-shilkov
View
937
Download
3
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
Here's how it works
PrinciplesConstraints
around yourcode
Bene잷�tsUseful
properties thathold under
theseconstraints
AdoptionSee where
these bene�tsmake thedi�erence
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?
public static int sqrtPlus1(int x) { if (x < 0) throw new ArgumentException(nameof(x)); return Math.Floor(Math.Sqrt(x)) + 1; }
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; }
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; }
Type signature as documentation
int ‐> int Customer ‐> Address U ‐> T[] ‐> U Identifier ‐> Task<Customer>
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
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); }
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); } }
Akka Streams
val counterRunnableGraph: RunnableGraph[Future[Int]] = tweetsInMinuteFromNow .filter(_.hashtags contains akkaTag) .map(t => 1) .toMat(sumSink)(Keep.right)
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
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)
Thanks!Mikhail Shilkov
Next: Introduction of F#
You can �nd me at @MikhailShilkov and http://mikhail.io