Functional programming concepts like lambda calculus and category theory can help organize code in C# and F#. Lambda calculus uses expressions instead of statements to represent computations. This emphasizes composability over side effects. Category theory provides a framework for reasoning about program structure using concepts like functors that map types and morphisms that represent functions. Functors allow abstracting different computation contexts like tasks and sequences. Monads can then combine computations across contexts through applicative functors. Overall, functional programming brings a mathematical approach to code organization that emphasizes composability, pure functions, and handling side effects through abstraction rather than mutation.
3. #dotNETSpain2015#dotNETSpain2015
Personal view of functional programming
Every 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 approach
Like: “Immutability is good because makes parallelism easier”
...but won't bore you with tough Maths
If only because I wouldn't be able to.
Disclaimer
4. #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?
5. #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?
6. #dotNETSpain2015#dotNETSpain2015
We all use Math to write performant code
Yes, those algorithms you copy from StackOverflow are Math ;)
Functional uses Math to organize your code
Or as others like to say, to reason about your code.
Two main topics...
Alonzo Church's Lambda Calculus
Category Theory
What is functional programming?
8. #dotNETSpain2015#dotNETSpain2015
Turing revisited
Only 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
9. Binding
int 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)
10. #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?
12. #dotNETSpain2015#dotNETSpain2015
Lambda Calculus uses only expressions
Did you notice statements were also missing in Alonzo's rules?
Statements are like void functions
They don't return a value, thus they only can have an influence in your
program through side-effects.
Statements are everywhere
Variable declaration
Conditionals: if/else, switch
Loops: for, do, while
Exception: try/catch
Scope: using
Void functions: I/O, state
Expressions vs Statements
13. 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.
14. Program flow
Side-effects break encapsulation.
Diagrams cannot be accurately
represented with statements.
Side-effects depend on mutable
state for every minor action (loops).
16. #dotNETSpain2015#dotNETSpain2015
Natural flow
Fluent APIs (e.g. LINQ) use expressions to chain operations naturally.
Code readability is improved (see next page: visual honesty).
Branching
Techniques like pattern matching allow natural branching of the code.
The Abstract Syntax Tree (AST) builds naturally.
Diagrams can be represented more accurately.
Pure functions
Strict 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
20. 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
);
}
}
21. 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");
}
}
22. 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 matching
type 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 methods
let 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-in
let main() =
match riskyFn() with
| Some i -> doSth i
| None -> ()
26. #dotNETSpain2015#dotNETSpain2015
You're already using monads
LINQ 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)
27. #dotNETSpain2015#dotNETSpain2015
A collection with three components
A collection of objects
A collection of morphisms
Composition 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 APIs
Task.Run(() => foo())
.ContinueWith(x => bar(x))
What is a category?
28. #dotNETSpain2015#dotNETSpain2015
CT provides us with a formal language
If 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 transformations
If 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?
29. #dotNETSpain2015#dotNETSpain2015
Functors map one category into another
It 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) // identity
F(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
30. 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 context
myTask.Select(x => x + x); // Asynchronous context
myNullable.Select(x => x + x); // Non-deterministic context
// In F#, most contexts implement the map function
// Instead of extension methods we often use module-bound functions
type 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 Nullable
let 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
31. #dotNETSpain2015#dotNETSpain2015
Abstraction is one pillar of functional
Everybody 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 composability
We can apply the same functions to different context or, at least, the same
programming style.
Why abstract the context?
32. #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
33. #dotNETSpain2015#dotNETSpain2015
Pure functions only take one argument
For 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
34. Applicative Functors in C#
// First lift the curried function to the domain of the context
public 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 operands
public 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);
}
// Test
Func<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
35. 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;
}
}
36. 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 = await
Async.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...
37. 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")
);
39. 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 too
module 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 )
40. 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"
41. #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 operation
Async → Extract the result of an asynchronous operation and perform
another asynchronous operation
Monad is coming
42. #dotNETSpain2015#dotNETSpain2015
So we need a transformation with the following signature:
M<a> Return<a>(a input) // Same as Pure in Applicatives
M<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
43. 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;
}
44. 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)
45. And this is how we use them
var 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
46. #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!
47. Monads revisited
// Extension method to use LINQ syntax with Task
public 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 syntax
await (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 easily
async { 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
48. #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...
52. #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 Wlaschin
The site to learn F# and functional programming, from an F# perspective
http://learnyouahaskell.com/
By Miran Lipovaca
A strict functional language helps you understand better the concepts
http://bugsquash.blogspot.com.es/
By Mauricio Scheffer
References