Jovan Popovic published an article on CodeProject regarding functional programming in C#.
I was planning to write a similar article. Thereby, I’m using the forum of this article to share some knowledge on functional programming in C#.
First of all, I strongly recommend reading this article. Especially for folks who are not familiar with functional programming basics, delegates, lambda expressions, Func, Predicate, Action, asynchronous functions and so on.
In this blog post, I will keep in sync the knowledge that I am sharing in the forum of this article. I hope that you will find these informations useful and If you have any question you can post comments on my blog or on the forum of the article.
Prerequisites
You will first need to read the article of Jovan Popovic on functional programming in C#. Then you will need some skills on LINQ. For LINQ, I advise you to read this article made by Jovan Popovic. You will also need some skills on Algebra, Design Patterns and Object Oriented Programming.
I advise folks who are not familiar with lambda expressions, expression trees, type inference, anonymous functions and delegates to read this article to get in touch with lambda expressions in 3 minutes and this article to cover things from syntax to constraints and implementation details for lambda expressions.
Map function
In many programming languages, Map is the name of a higher-order function that applies a given function element-wise to a list of elements and returns a list of results. They are examples of both catamorphisms and anamorphisms. This is often called apply-to-all when considered a functional form. Map function could also be written in C# with or without LINQ.
For instance, if we define a function square x => x * x, then calling map square [1,2,3,4,5] will return [1,4,9,16,25], as map will go through the list, and apply the function square to each element.
I’ve written 4 different implementations of this function in C#.
public static class Extension
{
// Apply a function f T1 -> T2 to each element of data using yield
public static IEnumerable<T2> Map1<T1, T2>(this IEnumerable<T1> data, Func<T1, T2> f)
{
foreach (T1 x in data) yield return f(x);
}
// Apply a function f T1 -> T2 to each element of data using Select
public static IEnumerable<T2> Map2<T1, T2>(this IEnumerable<T1> data, Func<T1, T2> f)
{
return data.Select(f);
}
// Apply a function f T1 -> T2 to each element of data using a List
public static IEnumerable<T2> Map3<T1, T2>(this IEnumerable<T1> data, Func<T1, T2> f)
{
List<T2> toReturn = new List<T2>();
foreach (T1 x in data) toReturn.Add(f(x));
return toReturn;
}
// Apply a function f T1 -> T2 to each element of data using Agrregate
public static IEnumerable<T2> Map4<T1, T2>(this IEnumerable<T1> data, Func<T1, T2> f)
{
return data.Aggregate(new List<T2>(), (l, e) => { l.Add(f(e)); return l; });
}
}
Fold
The Fold function is a higher-order function that analyze a recursive data structure and recombine through use of a given combining operation the results of recursively processing its constituent parts, building up a return value.
There are two types of Fold. FoldLeft and FoldRight. In the FoldLeft, the accumulation is done from the first element of the list to the last element. In the FoldRight the accumulation is done from the last element of the list to the first. FoldLeft is similar to enumerable.Aggregate and FoldRight is similar to enumarable.Reverse().Aggregate. The FoldLeft of the array [1,3,5,7] with the addition operation results in 16, the sum of the elements of the array [1,3,5,7].
You will find a complete description of the Fold function here.
Below the implementation of the Fold function in C#.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Fold
{
public static class Extension
{
public static T1 Fold<T1, T2>(this IEnumerator<T2> enumerator, T1 seed, Func<T1, T2, T1> f)
{
return enumerator.MoveNext()
? Fold<T1, T2>(enumerator, f(seed, enumerator.Current), f) // There is an element
: seed; // The list is empty
}
public static T1 FoldLeft<T1, T2>(this IEnumerable<T2> enumerable, T1 seed, Func<T1, T2, T1> f)
{
return enumerable.GetEnumerator().Fold<T1, T2>(seed, f);
}
public static T1 FoldRight<T1, T2>(this IEnumerable<T2> enumerable, T1 seed, Func<T1, T2, T1> f)
{
return enumerable.Reverse().GetEnumerator().Fold<T1, T2>(seed, f);
}
}
class Program
{
static string[] persons = new[] {
"Person1", "Person2", "Person3", "Person4", "Person5", "Person6", "Person7"
};
static void Main()
{
//
// Writing our operation
//
Func<string, string, string> operation =
(accumulator, person) => accumulator + person + " ";
//
// Writing a fold left with our function
//
Console.WriteLine("\nfold left with our function:");
string output = persons.FoldLeft(Environment.NewLine, operation);
Console.WriteLine(output);
//
// Writing a fold left with an aggregate
//
Console.WriteLine("\nfold left with an aggregate:");
output = persons.Aggregate(Environment.NewLine, operation);
Console.WriteLine(output);
//
// Writing a fold right with our function
//
Console.WriteLine("\nfold right with our function:");
output = persons.FoldRight(Environment.NewLine, operation);
Console.WriteLine(output);
//
// Writing a fold right with an aggregate
//
Console.WriteLine("\nfold right with an aggregate:");
output = persons.Reverse().Aggregate(Environment.NewLine, operation);
Console.WriteLine(output);
Console.ReadKey();
}
}
}
Useful functional extensions
noav posted useful extensions on the forum.
public static Func<T1, T2, TR> UnCurry<T1, T2, TR>(this Func<T1, Func<T2, TR>> func)
{
return (a, b) => func(a)(b);
}
public static Func<T2, TR> Partial<T1, T2, TR>(this Func<T1, T2, TR> func, T1 first)
{
return b => func(first, b);
}
public static void Use<T>(this T item, Action<T> action) where T : IDisposable
{
using (item)
{
action(item);
}
}
public static Func<T> Memorize<T>(this Func<T> func)
{
var value = default(T);
var hasValue = false;
return () =>
{
if (!hasValue)
{
value = func();
hasValue = true;
}
return value;
};
}
Visitor Design Pattern
The visitor design pattern is a way of separating operations from an object structure it operates on. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to easily follow the open/closed principle.
This pattern can be observed in the operation of a taxi company. When a person calls a taxi company (accepting a visitor), the company dispatches a cab to the customer. Upon entering the taxi the customer, or Visitor, is no longer in control of his or her own transportation, the taxi (driver) is.
You will find more details on the Visitor Design Pattern in this article Visitor Design Pattern.
Below the implementation of the taxi driver sample by using the Visitor Design Pattern in a functional way. I’ve done a minimalist visit operation by printing in the console the actions but you can of course perform more complex logic in the visit operation. You can also modify the signature of the visit delegate in order to return a type instead of void.
The taxi driver and the taxi company are visitable objects that accept visitors. The client is a visitor, its parameters are the parameters of a function and its visit operation is a function. When a client visits (call) a taxi or a taxi company, the taxi driver answers whether he is available or not. The taxi company looks for an available taxi driver to dispatch to the client. If the taxi company finds an available taxi driver, It dispatches it to the client and marks it as busy. Otherwise the taxi company asks the client to call again later. It is also possible to implement an asynchronous routine that marks a taxi driver as available when its job is finished and It is also possible to use a Round-Robin algorithm, load balancing, priority … to optimize the search for an available taxi driver depending on the location of the taxi driver depending on the address of the client.
By reading and understanding the code, you will see how easy it is to add new visitors, new operations and new algorithms without touching the visitable objects. The open closed, DRY and Liskov principles are easily respected through this functional implementation.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Visitor
{
//
// Generic Visitor
//
public delegate void Visitor<T>(T iTaxiDriver);
//
// Interface that is multiply inherited, and ready to accept a visitor
//
public interface ITaxiDriver
{
string Name { get; }
bool IsBusy { get; set; }
void Accept(Visitor<ITaxiDriver> visitor);
}
//
// Taxi driver
//
public class TaxiDriver : ITaxiDriver // Visitable
{
private string _name;
private bool _isBusy;
public TaxiDriver(string name, bool isBusy)
{
_name = name;
_isBusy = isBusy;
}
public string Name
{
get { return _name; }
}
public void Accept(Visitor<ITaxiDriver> visitor)
{
visitor(this);
}
public bool IsBusy
{
get { return _isBusy; }
set { _isBusy = value; }
}
}
//
// Group of taxi drivers
//
public class TaxiCompany : ITaxiDriver, IEnumerable<ITaxiDriver> // Visitable
{
private ITaxiDriver[] _drivers;
private string _Name;
private bool _isBusy;
public TaxiCompany(string name, ITaxiDriver[] drivers)
{
_Name = name;
_drivers = drivers;
}
public string Name
{
get { return _Name; }
}
public bool IsBusy
{
get { return _isBusy; }
set { _isBusy = value; }
}
public void Accept(Visitor<ITaxiDriver> visitor)
{
foreach (ITaxiDriver c in _drivers) c.Accept(visitor);
visitor(this);
_isBusy = !_drivers.Any(driver => !driver.IsBusy);
}
public IEnumerator<ITaxiDriver> GetEnumerator()
{
return new TaxiCompanyEnumerator(_drivers);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new TaxiCompanyEnumerator(_drivers);
}
}
public class TaxiCompanyEnumerator : IEnumerator<ITaxiDriver>
{
private ITaxiDriver[] _drivers;
//
// Enumerators are positioned before the first element
// until the first MoveNext() call.
//
int position = -1;
public TaxiCompanyEnumerator(ITaxiDriver[] drivers)
{
_drivers = drivers;
}
public bool MoveNext()
{
position++;
return (position < _drivers.Length);
}
public void Reset()
{
position = -1;
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public ITaxiDriver Current
{
get
{
try
{
return _drivers[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
//
// IEnumerator implements IDisposable to support some obscure, yet important
// scenarios where an enumerator enumerates database rows, or files in a directory, etc...
// In such cases, the enumerator usually opens some connection or a handle, which then needs
// to be closed at the end of the enumeration.
//
public void Dispose()
{
}
}
class Program
{
private static void Main()
{
ITaxiDriver[] drivers = new[]
{
new TaxiDriver("TaxiDriver 1", false),
new TaxiDriver("TaxiDriver 2", true),
new TaxiDriver("TaxiDriver 3", true),
new TaxiDriver("TaxiDriver 4", true),
new TaxiDriver("TaxiDriver 5", true),
new TaxiDriver("TaxiDriver 6", true),
new TaxiDriver("TaxiDriver 7", true),
};
//
// Construct the company
//
ITaxiDriver company =
new TaxiCompany("Taxi company A", drivers);
//
// Perform an operation
//
company.Accept(Visitor("Client", "Address"));
Console.ReadKey();
}
//
// Extension by a visitor function
// Separating the operation from the object structure on which it operates
//
public static Visitor<ITaxiDriver> Visitor(string name, string address)
{
return visitable =>
{
if (visitable is TaxiCompany)
{
TaxiCompany taxiCompany = (TaxiCompany)visitable;
if (taxiCompany.IsBusy)
{
Console.WriteLine(taxiCompany.Name + " is busy. Please call again later...");
}
else
{
//
// Here It is also possible to use a Round-Robin algorithm, load balancing,
// priority ... to optimize the search for an available taxi driver
// depending on the location of the taxi driver depending on the address of the client.
//
ITaxiDriver availableDriver = taxiCompany.First(driver => !driver.IsBusy);
Console.WriteLine("Dispatching " + availableDriver.Name + " to " + name + " to " + address);
availableDriver.IsBusy = true;
//
// Here we can launch an asynchronous routine that make the taxi driver available
// once his drive is finished.
//
}
}
else if (visitable is TaxiDriver)
{
if (visitable.IsBusy)
{
Console.WriteLine(visitable.Name + " is busy.");
}
else
{
Console.WriteLine(visitable.Name + " is available.");
}
}
};
}
}
}
Running the code:
TaxiDriver 1 is available.
TaxiDriver 2 is busy.
TaxiDriver 3 is busy.
TaxiDriver 4 is busy.
TaxiDriver 5 is busy.
TaxiDriver 6 is busy.
TaxiDriver 7 is busy.
Dispatching TaxiDriver 1 to Client to Address
Multiple inheritance
Multiple inheritance brings to a type the behavior of its parent types. I set up a simple example showing how to set up multiple inheritance through Tuple and Func.
I’ve created two base Tuples, Horse wich is defined by an operation Run, Bird wich is defined by an operation Fly and Pegasus wich is the combination of a Horse and a Bird (Multiple inheritance) extended by a name, and a power. Pegasus inherits from Horse and Bird, thus, it behaves like a Horse and like Bird, and it has extented parameters name and power.
This example is just a prototype that I decided to make as simple as possible, aiming to show how we could achieve multiple inheritance through the functional programming features provided by C#. Other functional programming languages like Haskell and OCaml provide multiple inheritance by default.
using System;
namespace FunctionalMultipleInheritancePrototype
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("\nHorse:");
var horse = Horse();
horse.Item1(); // I am running...
Console.WriteLine("\nBird:");
var bird = Bird();
bird.Item1(); // I am flying...
Console.WriteLine("\nPegasus:");
var pegasus = Pegasus("Super Pegasus", 7);
Console.WriteLine(pegasus.Item1 + " power is " + pegasus.Item2);
pegasus.Item3.Item1(); // I am running...
pegasus.Item4.Item1(); // I am flying...
Console.ReadKey();
}
//
// Super type Horse
//
static Tuple<Action> Horse()
{
Action Run = () => Console.WriteLine("I am running...");
return Tuple.Create(Run);
}
//
// Super type Bird
//
static Tuple<Action> Bird()
{
Action Fly = () => Console.WriteLine("I am flying...");
return Tuple.Create(Fly);
}
//
// Multiple inherited type Pegasus
//
static Tuple<string, int, Tuple<Action>, Tuple<Action>> Pegasus(string name, int power)
{
return Tuple.Create(name, // Name extension
power, // Power extension
Horse(), // Horse super type inheritance
Bird() // Bird Super type inheritance
);
}
}
}
Running the code:
Horse:
I am running…
Bird:
I am flying…
Pegasus:
Super Pegasus power is 7
I am running…
I am flying…
Useful Functional Extentions on 2D Generic Sets
using System;
using System.Collections.Generic;
using System.Linq;
namespace _2dSet
{
public static class Extensions
{
public static IEnumerable<T> GetColumn<T>(this IEnumerable<IEnumerable<T>> set, int columnNumber)
{
foreach (IEnumerable<T> row in set)
yield return row.Count() > columnNumber ? row.ElementAt(columnNumber) : default(T);
}
public static IEnumerable<T> GetRow<T>(this IEnumerable<IEnumerable<T>> set, int rowNumber)
{
return set.ElementAt(rowNumber);
}
public static IEnumerable<T2> SelectRow<T1, T2>(this IEnumerable<IEnumerable<T1>> set, int rowNumber, Func<T1, T2> selector)
{
return set.GetRow(rowNumber).Select(selector);
}
public static IEnumerable<T2> SelectColumn<T1, T2>(this IEnumerable<IEnumerable<T1>> set, int columnNumber, Func<T1, T2> selector)
{
return set.GetColumn(columnNumber).Select(selector);
}
public static T2 FoldLeftRow<T1, T2>(this IEnumerable<IEnumerable<T1>> set, int rowNumber, T2 seed, Func<T2, T1, T2> operation)
{
return set.GetRow(rowNumber).Aggregate(seed, operation);
}
public static T2 FoldLeftColumn<T1, T2>(this IEnumerable<IEnumerable<T1>> set, int columnNumber, T2 seed, Func<T2, T1, T2> operation)
{
return set.GetColumn(columnNumber).Aggregate(seed, operation);
}
public static T2 FoldRightRow<T1, T2>(this IEnumerable<IEnumerable<T1>> set, int rowNumber, T2 seed, Func<T2, T1, T2> operation)
{
return set.GetRow(rowNumber).Reverse().Aggregate(seed, operation);
}
public static T2 FoldRightColumn<T1, T2>(this IEnumerable<IEnumerable<T1>> set, int columnNumber, T2 seed, Func<T2, T1, T2> operation)
{
return set.GetColumn(columnNumber).Reverse().Aggregate(seed, operation);
}
public static void ActionRow<T>(this IEnumerable<IEnumerable<T>> set, int rowNumber, Action<T> action)
{
foreach (T e in set.GetRow(rowNumber)) action(e);
}
public static void ActionColumn<T>(this IEnumerable<IEnumerable<T>> set, int columnNumber, Action<T> action)
{
foreach (T e in set.GetColumn(columnNumber)) action(e);
}
public static void UseActionRow<T>(this IEnumerable<IEnumerable<T>> set, int rowNumber, Action<T> action) where T : IDisposable
{
foreach (T e in set.GetRow(rowNumber)) using (e) action(e);
}
public static void UseActionColumn<T>(this IEnumerable<IEnumerable<T>> set, int columnNumber, Action<T> action) where T : IDisposable
{
foreach (T e in set.GetColumn(columnNumber)) using (e) action(e);
}
public static bool PredicateRowAnd<T>(this IEnumerable<IEnumerable<T>> set, int rowNumber, Predicate<T> predicate)
{
return set.GetRow(rowNumber).All(e => predicate(e));
}
public static bool PredicateColumnAnd<T>(this IEnumerable<IEnumerable<T>> set, int columnNumber, Predicate<T> predicate)
{
return set.GetColumn(columnNumber).All(e => predicate(e));
}
public static bool UsePredicateRowAnd<T>(this IEnumerable<IEnumerable<T>> set, int rowNumber, Predicate<T> predicate) where T : IDisposable
{
return set.GetRow(rowNumber).All(e => { using (e) return predicate(e); });
}
public static bool UsePredicateColumnAnd<T>(this IEnumerable<IEnumerable<T>> set, int columnNumber, Predicate<T> predicate) where T : IDisposable
{
return set.GetColumn(columnNumber).All(e => { using (e) return predicate(e); });
}
public static bool PredicateRowOr<T>(this IEnumerable<IEnumerable<T>> set, int rowNumber, Predicate<T> predicate)
{
return set.GetRow(rowNumber).Any(e => predicate(e));
}
public static bool PredicateColumnOr<T>(this IEnumerable<IEnumerable<T>> set, int columnNumber, Predicate<T> predicate)
{
return set.GetColumn(columnNumber).Any(e => predicate(e));
}
public static bool UsePredicateRowOr<T>(this IEnumerable<IEnumerable<T>> set, int rowNumber, Predicate<T> predicate) where T : IDisposable
{
return set.GetRow(rowNumber).Any(e => { using (e) return predicate(e); });
}
public static bool UsePredicateColumnOr<T>(this IEnumerable<IEnumerable<T>> set, int columnNumber, Predicate<T> predicate) where T : IDisposable
{
return set.GetColumn(columnNumber).Any(e => { using (e) return predicate(e); });
}
}
class Program
{
static void Main(string[] args)
{
int[][] matrix = new int[][]{
new int[]{1,2,3,4,5,6,7},
new int[]{1,2,3,4,5,6,7},
new int[]{1,2,3,4,5,6,7},
new int[]{1,2,3,4,5,6,7},
new int[]{1,2,3,4,5,6,7},
new int[]{1,2,3,4,5,6,7},
new int[]{1,2,3,4,5,6,7}
};
Func<int, int> square = x => x * x;
Console.WriteLine("\nSelectColumn");
var columnSquare = matrix.SelectColumn(6, square);
foreach (int i in columnSquare) Console.WriteLine(i);
Console.WriteLine("\nSelectRow");
var rowSquare = matrix.SelectRow(0, square);
foreach (int i in rowSquare) Console.WriteLine(i);
Func<string, int, string> concat = (seed, i) => seed + i + " ";
Console.WriteLine("\nFoldLeftColumn");
string concatLeftColumn = matrix.FoldLeftColumn<int, string>(6, string.Empty, concat);
Console.WriteLine(concatLeftColumn);
Console.WriteLine("\nFoldLeftRow");
string concatLeftRow = matrix.FoldLeftRow<int, string>(0, string.Empty, concat);
Console.WriteLine(concatLeftRow);
Console.WriteLine("\nFoldRightColumn");
string concatRightColumn = matrix.FoldRightColumn<int, string>(6, string.Empty, concat);
Console.WriteLine(concatRightColumn);
Console.WriteLine("\nFoldRightRow");
string concatRightRow = matrix.FoldRightRow<int, string>(0, string.Empty, concat);
Console.WriteLine(concatRightRow);
Action<int> printSquare = i => Console.WriteLine(square(i));
Console.WriteLine("\nActionColumn");
matrix.ActionColumn(6, printSquare);
Console.WriteLine("\nActionRow");
matrix.ActionRow(0, printSquare);
Predicate<int> even = i => i % 2 == 0;
Predicate<int> odd = i => !even(i);
Console.WriteLine("\nPredicateColumnOr");
bool columnContainsEvenNumber = matrix.PredicateColumnOr(6, even);
Console.WriteLine(columnContainsEvenNumber
? "The column 6 contains an even number"
: "The column 6 doesn't contain an even number");
Console.WriteLine("\nPredicationRowOr");
bool rowContainsEvenNumber = matrix.PredicateRowOr(0, even);
Console.WriteLine(rowContainsEvenNumber
? "The row 0 contains an even number"
: "The row 0 doesn't contain an even number");
Console.WriteLine("\nPredicateColumnAnd");
bool columnIsOdd = matrix.PredicateColumnAnd(6, odd);
Console.WriteLine(columnIsOdd
? "All the digits of the column 6 are odd"
: "The column 6 contains an even number");
Console.WriteLine("\nPredicationRowAnd");
bool rowIsOdd = matrix.PredicateRowAnd(0, odd);
Console.WriteLine(rowIsOdd
? "All the digits of the row 0 are odd"
: "The row 0 contains an even number");
Console.ReadKey();
}
}
}
Running the code:
SelectColumn
49
49
49
49
49
49
49
SelectRow
1
4
9
16
25
36
49
FoldLeftColumn
7 7 7 7 7 7 7
FoldLeftRow
1 2 3 4 5 6 7
FoldRightColumn
7 7 7 7 7 7 7
FoldRightRow
7 6 5 4 3 2 1
ActionColumn
49
49
49
49
49
49
49
ActionRow
1
4
9
16
25
36
49
PredicateColumnOr
The column 6 doesn’t contain an even number
PredicationRowOr
The row 0 contains an even number
PredicateColumnAnd
All the digits of the column 6 are odd
PredicationRowAnd
The row 0 contains an even number
Lazy evaluation
Lazy evaluation is an evaluation strategy which delays the evaluation of an expression until its value is needed and which also avoids repeated evaluations.
The sharing can reduce the running time of certain functions by an exponential factor over other non-strict evaluation strategies, such as call-by-name.
Below the benefits of Lazy evaluation.
- Performance increases by avoiding needless calculations, and error conditions in evaluating compound expressions
- The ability to construct potentially infinite data structure: We can easily create an infinite set of integers for example through a function (see the first example in the C# code)
- The ability to define control flow (structures) as abstractions instead of primitives
Below the C# samples of lazy evaluation.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace LazyEvaluation
{
class Program
{
//
// Delay the evaluation an even number until its value is needed.
//
static IEnumerable<int> InfiniteEvenNumbersSet()
{
for (int i = 0; ; i++)
if (i % 2 == 0) yield return i;
}
static void Main(string[] args)
{
//
// Benefits of lazy evaluation:
//
// - Performance increases by avoiding needless calculations, and error conditions in evaluating compound expressions
// - The ability to construct potentially infinite data structures
// - The ability to define control flow (structures) as abstractions instead of primitives
//
//
// Lazy evaluation with yield
//
Console.WriteLine("\nyield:");
int i = InfiniteEvenNumbersSet().Skip(999).First();
Console.WriteLine("The 999 even number is " + i);
//
// Lazy evaluation with ThreadSafe Lazy<T>
// The list of numbers will be loaded only once
//
Console.WriteLine("\nLazy<T>:");
Func<int[]> getNumbers = () => new[] { 1, 2, 3, 4, 5, 6, 7 };
Lazy<int[]> lazyNumbers = new Lazy<int[]>(getNumbers, true);
// Numbers are not loaded yet
Console.WriteLine("Are Numbers loaded? {0}", lazyNumbers.IsValueCreated);
// Load the numbers
// This executes getNumbers()
int k;
k = lazyNumbers.Value.Sum();
Console.WriteLine("The sum of integers from 1 to 7 is " + k);
if (lazyNumbers.IsValueCreated)
{
Console.WriteLine("Are Numbers loaded? {0}", lazyNumbers.IsValueCreated);
k = lazyNumbers.Value.Sum(); // getNumbers() is not executed because it is exectued only once
Console.WriteLine("The sum of integers from 1 to 7 is {0} (Lazy loading)", k);
}
//
// Lazy evaluation with ThreadLocal<T>
//
Console.WriteLine("\nThreadLocal<T>:");
using (ThreadLocal<int[]> lazyNumbersViaThreadLocal = new ThreadLocal<int[]>(getNumbers))
{
// multi core sample
Action printSum = () =>
{
bool isLoaded = lazyNumbersViaThreadLocal.IsValueCreated;
Console.WriteLine("Are Numbers loaded? {0}", isLoaded);
// Load the numbers
// This executes getNumbers()
k = lazyNumbersViaThreadLocal.Value.Sum();
Console.WriteLine("The sum of integers from 1 to 7 is {0} {1}", k,
isLoaded ? "(Lazy Loading)" : string.Empty);
};
Parallel.Invoke(printSum, printSum);
}
//
// Lazy evaluation with LazyInitializer
//
Console.WriteLine("\nLazyInitializer:");
int[] myNumbers = null;
bool areNumbersLoaded = false;
object locker = new object();
int[] myLazyNumbers = LazyInitializer.EnsureInitialized<int[]>(ref myNumbers, ref areNumbersLoaded, ref locker, getNumbers);
k = myLazyNumbers.Sum();
Console.WriteLine("The sum of integers from 1 to 7 is " + k);
if (areNumbersLoaded)
{
Console.WriteLine("Are Numbers loaded? {0}", areNumbersLoaded);
k = myLazyNumbers.Sum(); // getNumbers() is not executed because it is exectued only once
Console.WriteLine("The sum of integers from 1 to 7 is {0} (Lazy loading)", k);
}
// Func<int, double> f = x => x / 0;
Console.ReadKey();
}
}
}
Running the code:
yield:
The 999 even number is 1998
Lazy:
Are Numbers loaded? False
The sum of integers from 1 to 7 is 28
Are Numbers loaded? True
The sum of integers from 1 to 7 is 28 (Lazy loading)
ThreadLocal:
Are Numbers loaded? False
The sum of integers from 1 to 7 is 28
Are Numbers loaded? True
The sum of integers from 1 to 7 is 28 (Lazy Loading)
LazyInitializer:
The sum of integers from 1 to 7 is 28
Are Numbers loaded? True
The sum of integers from 1 to 7 is 28 (Lazy loading)
In some functional programming languages like OCaml, the lazy evaluation could be done on functions and on objects. For instance, If we have a lazy function f: x => x / 0, then If we create a lazy list of integers [f(1), f(2), f(3)] the execution of the function is differed to execution time when we force the loading of a lazy element from the list. In other words, the calculation of the function is done only when we force its execution.
In C#, such features are not provided yet because the eveluation of the lambda is done at compile time. If we write a similar lambda in C# Func
Purely functional functions
Purely functional functions have no side effects. For example, square root, sinus, and cosinus are pure functions. Whereas File.Open is an impure function. Pure functions have several useful properties that optimize the code:
- If the result of a pure expression is not used, it can be removed without affecting other expressions.
- If a pure function is called with parameters that cause no side-effects, the result is constant with respect to that parameter list.
- If there is no data dependency between two pure expressions, then their order can be reversed, or they can be performed in parallel and they cannot interfere with one another.
Pure algorithms, data structures or expressions exclude destructive modifications. Thereby, variables are used in a mathematical sense, with identifiers referring to immutable, persistent values.
C# doesn’t provide side-effect checking for general use yet, Pure attribute has been introduced in Code Contracts. But it is dedicated for use with the Contract API’s pre-condition and post-condition checking rather than general use.
