functional programming in c# and f#

Post on 14-Jul-2015

1.554 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

#dotNETSpain2015

Alfonso García-Caro@alfonsogcngithub.com/alfonsogarciacaro/dotNetSpain2015

Functional Programmingin C# and F#

.NET Conference 2015

Y

AX B

#dotNETSpain2015#dotNETSpain2015

Software Engineer at Green Eagle Solutions.Net and Web developerCollaborator of FunScript projectOrganizer of Madrid .Net and F# Meetups

About me

#dotNETSpain2015#dotNETSpain2015

Personal view of functional programmingEvery language and every programmer understands functional programming a bit differently. There will also be some simplifications.

Bias towards F# but I also love C#The sentence that will repeat most in this talk:“possible in C#, idiomatic in F#”

Not an utilitarian-only approachLike: “Immutability is good because makes parallelism easier”

...but won't bore you with tough MathsIf only because I wouldn't be able to.

Disclaimer

#dotNETSpain2015#dotNETSpain2015

Juan Pedro Villa Isaza, Category Theory applied to Functional Programming

[…] we are considering the objects and morphisms of Set to be the sets of all sets and all functions, respectively, which would lead to a paradox such as the set of all sets not members of themselves. For this reason, we ought to assume, for instance, that there is a big enough set, the universe, and take the objects of Set to be the sets which are members of the universe, that is, small sets. However, we shall not go into detail about mathematical foundations of category theory(*).

(*) Thank goodness!

Can functional really help me?

#dotNETSpain2015#dotNETSpain2015

My boss, at the office

Download real-time market price data from the internet at periodical intervals, insert it into the database and throw an event if the next hour price is lower than the threshold. In that case, communicate with the SCADA system through the OPC protocol to stop the wind turbines and check if the response has been successful.

Is there really anyway the former can help me with the latter?

Can functional really help me?

#dotNETSpain2015#dotNETSpain2015

We all use Math to write performant codeYes, those algorithms you copy from StackOverflow are Math ;)

Functional uses Math to organize your codeOr as others like to say, to reason about your code.

Two main topics...

Alonzo Church's Lambda CalculusCategory Theory

What is functional programming?

#dotNETSpain2015

Lambda CalculusIt's the composability, ***

#dotNETSpain2015#dotNETSpain2015

Turing revisitedOnly three rules to express all possible computations

(e stands for expression)Examples of lambdas:

e ::= x Reference Func<int, int> f = x => x + x; // C#

| λx.e Definition let f x = x + x // F#

| e e Application let f' = fun x -> x + x // F#

Wait, there is reference but no assignment?In strict functional there's no assignment, only binding.Binding is the same as definition an application at once.There's no re-binding either, and thus mutability is an alien concept.

Alonzo Church's Lambda Calculus

Bindingint add5(int x) { var five = 5; // Assigning a variable in local scope return x + five; // and performing a computation with it...}

int add5_(int x) { Func<int, int> f = five => x + five; // ...is the same as defining a function return f(5); // and applying it}

int add5__(int x) { var five = 5; // Look again: this is the parameter return x + five; // and this is the body of the function}

let add5 x = let five = 5 // Now in F# x + five // These three blocks are identical

let add5' x = (fun five -> x + five)(5) // Definition and application

let add5'' x = let five = 5 in // With the verbose syntax, you can see x + five // the body of the binding more easily

let add5''' x = let five = 5 // Error: binding body missing (in local scope)

#dotNETSpain2015#dotNETSpain2015

Q. Is this not a limitation to the expressiveness of the language?

A. Maybe, but if you look around you'll see that this design pattern is very common in nature.

Fractal theory

Complex design emerge from simple patterns.

Every element has very few dependencies.

Same structure at all levels: expression < function < module/file < library

Why few rules is a good thing?

Fractals in nature

#dotNETSpain2015#dotNETSpain2015

Lambda Calculus uses only expressionsDid you notice statements were also missing in Alonzo's rules?

Statements are like void functionsThey don't return a value, thus they only can have an influence in your program through side-effects.

Statements are everywhereVariable declarationConditionals: if/else, switchLoops: for, do, whileException: try/catchScope: usingVoid functions: I/O, state

Expressions vs Statements

Program flow

This is how the program flow looks with staments.

Notice there's no return arrow.

“Well, it doesn't look so bad”

Really?

Let's see it with side-effects.

Program flow

Side-effects break encapsulation.

Diagrams cannot be accurately represented with statements.

Side-effects depend on mutable state for every minor action (loops).

Alternatives to statements

Conditionals → ?: operator

Switch → Pattern Matching

Loops → LINQ, recursion

Exceptions → Monads

#dotNETSpain2015#dotNETSpain2015

Natural flowFluent APIs (e.g. LINQ) use expressions to chain operations naturally.Code readability is improved (see next page: visual honesty).

BranchingTechniques like pattern matching allow natural branching of the code.The Abstract Syntax Tree (AST) builds naturally.Diagrams can be represented more accurately.

Pure functionsStrict functional only allows pure functions.Pure functions always return a value and don't have side-effects.They don't break the program flow.

Expression merits

Visual Honesty with Fluent APIs

Source: www.theburningmonk.com

Pattern MatchingThe feature that sacrificed itself so C# 6.0 could be released on time

Pattern Matching

Switch on steroids

Pattern Matching// With some effort you can still get switch expressions in C#enum Pet { Dog, Cat, Bird }

static class Extensions { public static T Switch<T>(this Pet pet, Func<T> dog, Func<T> cat, Func<T> bird) { switch (pet) { case Pet.Dog: return dog(); case Pet.Cat: return cat(); case Pet.Bird: return bird(); default: throw new Exception("Inexhaustive matching"); } }}

class Program { public int HowOldIsMyPet(int animalYears, Pet pet) { return pet.Switch( dog: () => animalYears * DOG_YEARS, cat: () => animalYears * CAT_YEARS, bird:() => animalYears * BIRD_YEARS ); }}

Pattern Matching // Or even Union Types (aka Sum or Algebraic Types) public abstract class Option<T> { public readonly T Value; Option() { } Option(T value) { Value = value; }

public sealed class Some : Option<T> { public Some(T value) : base(value) { } } public sealed class None : Option<T> { public None() : base() { } }

public TOutput Match<TOutput>(Func<T, TOutput> some, Func<TOutput> none) { return this is Some ? some(Value) : none(); } }

class Program { public Option<string> foo() { return new Option<string>.Some("Foo!"); }

public string bar() { return foo().Match( some: name => "FOUND: " + name, none: () => "NOTHING"); } }

Pattern Matching// In F# these constructs are idiomatic and the syntax is much terser// Most of the times we use union types with Pattern Matching// Union types look like enum, but they can actually contain any value// We can extract this value when pattern matchingtype Pet = | Dog of int | Cat of int | Bird of int

let howOldIsMyPet pet years = match pet with | Dog dy -> years * dy | Cat cy -> years * cy | Bird by -> years * by

// Dummy methodslet riskyFn(): int option = failwith "Not implemented"let doSth(i: int): unit = failwith "Not implemented"

// Option type is a particular union type and it's built-inlet main() = match riskyFn() with | Some i -> doSth i | None -> ()

Syntax Trees are also in Nature

#dotNETSpain2015

Category TheoryRoad to Monads

Road to perdition?

#dotNETSpain2015#dotNETSpain2015

You're already using monadsLINQ and Task are also monads. Sort of.

If I already use them, do I need the theory?Think of it as the garbage collector, when you try to learn a bit about it, you can use it more efficiently.

But, what is category anyway?Is it not the same as a set?

Good news! (maybe)

#dotNETSpain2015#dotNETSpain2015

A collection with three componentsA collection of objectsA collection of morphismsComposition of the morphisms, which must be associative

How do they map to C#/F#?Objects are types (Attention!)Morphisms are functions

In F#, composition is the >> operator(f >> g)(x) = f(g(x))

In C#, composition is often represented with fluent APIsTask.Run(() => foo()) .ContinueWith(x => bar(x))

What is a category?

#dotNETSpain2015#dotNETSpain2015

CT provides us with a formal languageIf the program needs to be deterministic, we need a deterministic way to think about it.Obeying the laws it dictates can give us a proof of correctness.More practically, we can generate random tests to check whether the expected properties of our types are being fulfilled (FSCheck).

CT teaches us about transformationsIf our types and functions (categories) follow certain laws, we know we can apply some specific transformations.This is great, because there are many categories following these rules which we are not usually aware of.We are going to consider these categories as contexts as in non-deterministic, sequential or asynchronous contexts.

What is Category Theory useful for?

#dotNETSpain2015#dotNETSpain2015

Functors map one category into anotherIt could be represented as an interface like this (pseudo code):

F<a> Map<F,a,b>(this F<a> input, Func<a,b> f) // C#map: ('a → 'b) → 'F<'a> → 'F<'b> // F#

The Functor must follow these rules:F(id a) = id(F a) // identityF(f >> g) = F(f) >> F(g) // composition

Attention! The language doesn't enforce these rules, we must check them ourselves... but this is usually a work for library writers :)

The .NET type system doesn't allow this signature. However many contexts (say, generic types) implement it. Do you recognize it?

Our first transformation: the Functor

Functors in C# and F#public static Task<TOutput> Select<T, TOutput>(this Task<T> input, Func<T, TOutput> f) { return input.ContinueWith(t => f(t.Result));}

public static Nullable<TOutput> Select<T, TOutput>(this Nullable<T> input, Func<T, TOutput> f) where TOutput : struct { return input.HasValue ? new Nullable<TOutput>(f(input.Value)) : new Nullable<TOutput>();}

myList.Select(x => x + x); // Sequential contextmyTask.Select(x => x + x); // Asynchronous contextmyNullable.Select(x => x + x); // Non-deterministic context

// In F#, most contexts implement the map function// Instead of extension methods we often use module-bound functionstype Async<'a> with static member map f x = async { let! y = x in return f y }

let myList = [1; 2; 3;]let myOption: Option<int> = None // Option is preferred over Nullablelet myAsync: Async<int> = async { return 1 } // Async is prererred over Task

List.map (fun x -> x + x) myList // Functions can be chained with |> or >>Option.map (fun x -> x + x) myOption // myList |> List.map f |> List.map f'Async.map (fun x -> x + x) myAsync // List.map (f >> g) myList

#dotNETSpain2015#dotNETSpain2015

Abstraction is one pillar of functionalEverybody wants to program in a synchronous, deterministic context.

We don't need to worry about the implementation details of translating our computation to the context, the library does it for us.

This is may be trivial in certain contexts (IEnumerable, Nullable) but not in others (concurrent and parallel programming). The garbage collector for example has been abstracting the context of heap memory for years.

Abstraction favors composabilityWe can apply the same functions to different context or, at least, the same programming style.

Why abstract the context?

#dotNETSpain2015#dotNETSpain2015

What if we want to combine several contexts?With map we can only apply a computation to a specific context.

Sometimes we need to apply a function to several contexts at once. For example if we need to check all nullable fields of a UI form.

Then we need to move one step further and see Applicative Functors.

But before that we need to tackle another topic...

Partial applications

When mapping is not enough

#dotNETSpain2015#dotNETSpain2015

Pure functions only take one argumentFor example, when we see a function like this in F#:let f x y = x + y(We can also pass tuples, but let's focus on partial application no.)

It would translate to C# like:Func<int, Func<int, int>> f = x => y => x + y;

This way, if we apply only one argument we get a partially applied function:var add5 = f(5); // add5 == x => x + 5;

Why would we need that?Composability, remember?Anyway, with this knowledge we can keep on.

Partial applications

Applicative Functors in C#

// First lift the curried function to the domain of the contextpublic static IEnumerable<a> Pure<a>(a input) { return new a[] { input };}

// Then apply the function to the first operand. As the result is a partially applied// function, we can still apply it to the rest of the operandspublic static IEnumerable<b> Apply<a, b>(IEnumerable<Func<a, b>> liftedFn, IEnumerable<a> input) { foreach (var f in liftedFn) foreach (var x in input) yield return f(x);}

// TestFunc<int, Func<int, int>> add = x => y => x + y;var lifted1 = Pure(add);var lifted2 = Apply(lifted1, new int[] { 1, 2 } );Apply(lifted2, new int[] { 3, 4 }); // Output: 4, 5, 5, 6

Applicative Functors in C# public static partial class Option { public static Option<a> Pure<a>(a input) { return new Option<a>.Some(input); }

public static Option<b> Apply<a, b>(Option<Func<a, b>> liftedFn, Option<a> input) { return liftedFn.Match( some: f => Option.Map(input, f), none: () => new Option<b>.None() ); } }

public static partial class Async { public static Task<a> Pure<a>(a input) { return Task.Run(() => input); }

/// <summary>Caution: Simplified code, no error nor cancellation checks</summary> public static Task<b> Apply<a, b>(Task<Func<a, b>> liftedFn, Task<a> input) { var tcs = new TaskCompletionSource<b>(); liftedFn.ContinueWith(f => input.ContinueWith(x => tcs.SetResult(f.Result(x.Result)) )); return tcs.Task; } }

Applicative Functors in C#// Downloading several web pages using the Applicative Functor var downloader = new WebPageDownloader();Func<string, Task<string>> fetch10 = url => downloader.FetchLinesAsync(url, 10);

Func<string, Func<string, Func<string, string>>> curry = x => y => z => x + y + z;

var res1 = awaitAsync.Apply( Async.Apply( Async.Apply( Async.Pure(curry), fetch10("http://microsoft.github.io")), fetch10("http://fsharp.org")), fetch10("http://funscript.info"));

// Obviously, the syntax is not very pretty.// We can simplify it by providing methods to lift// functions with a fixed number of arguments.// Like...

Applicative Functors in C#public static partial class Async { // We can take the chance to add some optimizations (like parallelism) // But let's keep it simple for now public static Task<e> Lift3<a, b, c, e>(Func<a, b, c, e> f, Task<a> x, Task<b> y, Task<c> z){ Func<a, Func<b, Func<c, e>>> curry =

_x => _y => _z => f(_x, _y, _z);

var lifted1 = Pure(curry); var lifted2 = Apply(lifted1, x); var lifted3 = Apply(lifted2, y); return Apply(lifted3, z); }}

// Now the previous code can be rewritten as:await Async.Lift3( (x,y,z) => x+y+z, fetch10("http://microsoft.github.io"), fetch10("http://fsharp.org"), fetch10("http://funscript.info"));

#dotNETSpain2015#dotNETSpain2015

Source: http://www.slideshare.net/ScottWlaschin/

Lifting is not only for Hollywood stars

Applicative Functors in F#// In F#, thanks to type inference and other features the syntax is much terser// Also, many of the functions we need are already implemented// We can define our custom operators for an even lighter syntax toomodule Seq = let ``pure`` f = seq { yield f } // pure is a keyword in F#

let (<*>) f a = // <*> is often used for applying seq { for f in f do for a in a do yield f a } let (<!>) f a = ``pure`` f <*> a // Convenience operator

let f x y = x + y // Quick tests printfn "%A" (f <!> [3;4] <*> [2;1]) printfn "%A" (f <!> [3;4] <*> [])

module Option = let (<*>) f a = match f with | Some f -> Option.map f a | None -> None

let ``pure`` f = Some f let (<!>) f a = ``pure`` f <*> a

printfn "%A" ((+) <!> Some 5 <*> Some 4) // Quick tests printfn "%A" ((+) <!> Some 5 <*> None )

Applicative Functors in F#module Async = let ``pure`` f = async { return f } let (<*>) f a = async { let! f = f let! a = a return f a } let (<!>) f a = ``pure`` f <*> a

// Downloading content from the internet let fetchLines (url: string) i = async { let rec append i (s: StreamReader) (b: StringBuilder) = match i with | 0 -> b.ToString() | _ -> s.ReadLine() |> b.AppendLine |> append (i-1) s let req = WebRequest.Create(url) use! resp = req.AsyncGetResponse() use stream = resp.GetResponseStream() use reader = new StreamReader(stream) return append i reader (StringBuilder()) }

(fun x y z -> String.length x + String.length y + String.length z) <!> fetchLines "http://microsoft.github.io" 10 <*> fetchLines "http://fsharp.org" 10 <*> fetchLines "http://funscript.info" 10 |> Async.RunSynchronously |> printfn "Chars fetched: %i"

#dotNETSpain2015#dotNETSpain2015

What's a monad anyway?A monad is a monoid in the category of endofunctors, what's the problem? http://james-iry.blogspot.com.es/2009/05/brief-incomplete-and-mostly-wrong.html

What's a monad useful for?We've just seen how to lift a function so it can be applied to operands “wrapped” in a context. However, there are many cases when we want to unwrap an operand and return a result already in the context. Think of:

Sequential → Extract an element from a collection and return a collection (LINQ SelectMany())Non-deterministic → Extract the value of Nullable, Option, Either and return the result of a risky operationAsync → Extract the result of an asynchronous operation and perform another asynchronous operation

Monad is coming

#dotNETSpain2015#dotNETSpain2015

So we need a transformation with the following signature:

M<a> Return<a>(a input) // Same as Pure in ApplicativesM<b> Bind<a,b>(M<a> input, Func<a,M<b>> f)

return: 'a → 'M<'a>bind: 'a → ('M<'a> → 'M<'b>) → 'M<'b>

The return function is used to “leave the monad.”As monadic expressions are just abstract computations that we need later to apply into a context, the return value must be wrapped in the context.Thus, we have to “wrap” the result before leaving the monad.

(Depending on how the language implements the concept of monad, we may need to implement other functions. But let's ignore them for now.)

Mapping vs Binding

Monads in C#public static partial class Seq { public static IEnumerable<a> Return<a>(a input) { return Pure(input); } public static IEnumerable<b> Bind<a, b>(IEnumerable<a> input, Func<a, IEnumerable<b>> f) { return input.SelectMany<a, b>(f); }}

public static partial class Option { public static Option<a> Return<a>(a input) { return Pure(input); } public static Option<b> Bind<a, b>(Option<a> input, Func<a, Option<b>> f) { return input.Match( some: v => f(v), none: () => new Option<b>.None() ); }}

public static partial class Async { public static Task<a> Return<a>(a input) { return Pure(input); } /// <summary>Caution: Simplified code, no error nor cancellation checks</summary> public static Task<b> Bind<a, b>(Task<a> input, Func<a, Task<b>> f) { var tcs = new TaskCompletionSource<b>(); input.ContinueWith(x => f(x.Result).ContinueWith(y => tcs.SetResult(y.Result))); return tcs.Task; }

Monads in F#module Seq = let ``return`` f = seq { yield f }

let (>>=) xs f = // This is the usual operator for bind seq { for x in xs do yield! f x }

module Option = let ``return`` f = Some f

// Already implemented in FShap.Core let (>>=) a f = match a with | Some a -> f a | None -> None

module Async = // Already implemented in FShap.Core let ``return`` = async.Return let (>>=) a f = async.Bind(a, f)

And this is how we use themvar downloader = new WebPageDownloader(); // C#Func<string, Task<string>> fetch10 = url => downloader.FetchLinesAsync(url, 10);

var res1 = await Async.Bind( fetch10("http://microsoft.github.io"), x => Async.Bind( fetch10("http://fsharp.org"), y => Async.Bind( fetch10("http://funscript.info"), z => Async.Return(x + y + z) ) ));

let fetchLines (url: string) i = async { // F# let rec append i (s: StreamReader) (b: StringBuilder) = match i with | 0 -> b.ToString() | _ -> s.ReadLine() |> b.AppendLine |> append (i-1) s let req = WebRequest.Create(url) use! resp = req.AsyncGetResponse() use stream = resp.GetResponseStream() use reader = new StreamReader(stream) return append i reader (StringBuilder())}async.Bind(fetchLines "http://microsoft.github.io" 10, fun x -> async.Bind(fetchLines "http://fsharp.org" 10, fun y -> async.Bind(fetchLines "http://funscript.info" 10, fun z -> async.Return(x + y + z))))|> Async.RunSynchronously

#dotNETSpain2015#dotNETSpain2015

Is this any advance at all?Monads can be really useful, but nesting function is not pretty and soon leads to “callback hell.”

That's why most languages implementing monads add a macro system (sugar syntax) in order to make them more pleasant.

In C# we can use extension methods or even abuse the LINQ special syntax.

In F# we have computation expressions, designed for monads but including also many extra features:

Generators seq { for x in xs do yield x + x }Query expressions query { from x in db do select x }Macros for .NET constructs use!

But this is horrible!

Monads revisited// Extension method to use LINQ syntax with Taskpublic static Task<b> Select<a, b>(this Task<a> input, Func<a, b> f) { return Async.Map(input, f);}

public static Task<c> SelectMany<a,b,c>( this Task<a> input, Func<a, Task<b>> f, Func<a, b, c> projection) {

return Bind(input, outer => Bind(f(outer), inner => Return(projection(outer, inner))));}

// Now we can concatenate async operations using LINQ syntaxawait (from x in fetch10("http://microsoft.github.io") from y in fetch10("http://fsharp.org") let url = "http://funscript.info" from z in fetch10(url) select x + y + z);

// In F# the boiler plate code has already been done for us in Async// But we can implement our own custom computation expressions easilyasync { let! x = fetchLines "http://microsoft.github.io" 10 let! y = fetchLines "http://fsharp.org" 10 let url = "http://funscript.info" let! z = fetchLines url 10 return x + y + z }|> Async.RunSynchronously

#dotNETSpain2015#dotNETSpain2015

Why monads seems to be some pervasive in functional programming?Why I don't hear to talk about functors or applicatives so much?

Monads are an elegant idea. Same as map/reduce they can be used to deal in a similar fashion with many different problems.

In strict functional programming they have been used to tackle side-effects, for example: IO, State monads

We can also use the F# Async monad to code state machines without flags.

Feel the monad!

OK, I get the idea but...

State machines without state?

The state is in the program flow

#dotNETSpain2015

Now you're ready to join the functional army!

#dotNETSpain2015#dotNETSpain2015

#dotNETSpain2015#dotNETSpain2015

http://ericlippert.com/2013/02/21/monads-part-one/Monads and C#, by Eric Lippert

Real-World Functional Programming, with examples in F# and C#By Tomas Petricek and Jon Skeet

http://fsharpforfunandprofit.com/By Scott WlaschinThe site to learn F# and functional programming, from an F# perspective

http://learnyouahaskell.com/By Miran LipovacaA strict functional language helps you understand better the concepts

http://bugsquash.blogspot.com.es/By Mauricio Scheffer

References

top related