SlideShare a Scribd company logo
WIESŁAW KAŁKUS
Holte Software
Poland
Functional Programming
Is it possible in C#?
Today’s Agenda
C# Functional Programming
Is It Worth An Effort? Maybe It Is
Controlling Complexity
Composition Function Composition
Programming Example
Copying Photo Files Implementation Variants
What Is Functional Programming?
Theoretical Perspective C# Programmer Perspective
What is Functional
Programming?
What Is Functional Programming?
Wikipedia
Functional Programming is a programming paradigm,
a style of building the structure and elements of
computer programs, that treats computations as the
evaluation of mathematical functions and avoids
changing-state and mutable data. It is a declarative
programming paradigm, which means programming is
done with expressions.
Programming Paradigms
• Focus on describing how a program operates
• Programs described in terms of statements that change a program
state
• Structural (procedures as main actors) and object-oriented (objects as
main actors) programming styles fall into imperative paradigm
Imperative
• Focus on describing what a program should accomplish without
prescribing how to do it in terms of sequences of actions to be taken
• Programs expressed in terms of computation logic without describing
its control flow
• Functional (mathematical function as main actor) programming style
falls into declarative paradigm
Declarative
Multi-paradigm programming languages
Type inference (C# & F#)
Anonymous functions (C# & F#)
Language Integrated Query (C#
& F#)
Currying (F#)
Pattern matching (F#)
Lazy evaluation (F#)
Tail recursion (F#)
Immutability (F#)
Control statements (C#)
Classes (C#)
.NET framework (C# & F#)
Do notation (F#)
Declarative
Imperative
Object-Oriented vs. Functional Programming Style
Characteristic Object-oriented (imperative) approach Functional (declarative) approach
Programmer focus
How to perform tasks (algorithms) and
how to track changes in state.
What information is desired and what
transformations are required.
State changes Important. Localized and tightly controlled.
Order of execution Important. Low importance.
Primary flow control
Loops, conditionals, and function
(method) calls.
Function calls, including recursion.
Primary manipulation unit Instances of structures or classes.
Functions as first-class objects and data
collections.
Programming Example
Programming Example – problem statement
Copy photo files from the source folder (include subfolders) to the
destination folder (create subfolders as necessary). While copying
rename each file using pattern <base-name><sequence-number>.
Sequence numbers should preserve the order of date taken for each
photo. Additionally allow for each source photo file to create multiple
copies of the file in the target folder (useful for extending time-lapse
photography sequences).
Programming Example – object-oriented approach
foreach (sourceFile in sourceFilesSortedByPhotoDateTaken)
{
try
{
CreateTargetFolderPathIfNecessary
fileCopyCount = 0
while (fileCopyCount < PhotoFileCopyMultiplicationFactor)
{
MakeNewTargetFileName
CopySourceFileToTheTargetFolderUnderTheNewName
fileCopyCount += 1
}
}
catch
{
}
}
Programmer focus on how to perform
tasks (algorithms) and how to track
changes in state.
Programming Example – object-oriented approach
foreach (sourceFile in sourceFilesSortedByPhotoDateTaken)
{
try
{
CreateTargetFolderPathIfNecessary
fileCopyCount = 0
while (fileCopyCount < PhotoFileCopyMultiplicationFactor)
{
MakeNewTargetFileName
CopySourceFileToTheTargetFolderUnderTheNewName
fileCopyCount += 1
}
}
catch
{
}
}
var sourceFiles = .Visit (sourceFolder);
sourceFiles.Sort( .ByDateTaken);
foreach (var sourceFile in sourceFiles) {
try {
CreateTargetFolderPathIfNecessary
fileCopyCount = 0
while (fileCopyCount < PhotoFileCopyMultiplicationFactor) {
MakeNewTargetFileName
CopySourceFileToTheTargetFolderUnderTheNewName
fileCopyCount += 1
}
}
catch{
}
}
public static class FolderVisitor {
public static List<FileInfo> Visit(string rootFolder) {
return DoVisitFolder(rootFolder);
}
private static List<FileInfo> DoVisitFolder(string folder) {
var files = new List<FileInfo>();
var thisFolder = new DirectoryInfo(folder);
files.Add(thisFolder.EnumerateFiles());
var subFolders = thisFolder.EnumerateFolders();
foreach (var folder in subFolders) {
files.Add(DoVisitFolder(subfolder.FullName));
}
return files;
}
}
public static class Photo {
public static int ByDateTaken (FileInfo f1, FileInfo f2) {
if (f1 == null && f2 == null) {
return 0;
} else if (f1 == null) {
return -1;
} else if (f2 == null) {
return 1;
}
return Photo.DateTaken(f1).CompareTo(Photo.DateTaken(f1));
}
private static DateTime DateTaken(FileInfo f) {
DateTime dateTaken = DateTime.Now;
try {
using (var reader = ExifReader (f.FullName)) {
reader.GetTagValue<DateTime>(
ExifTags.DateTimeDigitized,
our dateTaken);
}
}
catch (IOException) {}
return dateTaken;
}
}
Add files from the
current folder.
Recursively add files
from subfolders.
Open source Exif Library
– read EXIF tag from the
photo file.
Programming Example – object-oriented approach
var sourceFiles = FolderVisitor.Visit (sourceFolder);
sourceFiles.Sort(Photo.ByDateTaken);
foreach (var sourceFile in sourceFiles) {
try {
CreateTargetFolderPathIfNecessary
fileCopyCount = 0
while (fileCopyCount < PhotoFileCopyMultiplicationFactor) {
MakeNewTargetFileName
CopySourceFileToTheTargetFolderUnderTheNewName
fileCopyCount += 1
}
}
catch{
}
}
public static class FolderHelper {
public static void MaterializeFolder(string root string path) {
var realtivePath = path.SubString(root.Length);
if (string.IsNullOrWhitespace(relativePath)) {
return;
}
var segments = relativePath.Split(new char[] {
Path.DirectorySeparatorChar,
Path.AltDirectorySeparatorChar
}, StringSplitOptions.RemoveEmptyEntries);
var workingPath = root;
foreach (var segment in segments) {
workingPath = Path.Combine(workingPath, segment);
if (!Directory.Exists(workingPath)) {
Directory.CreateDirectory(workingPath);
}
}
}
}
Create missing
folders.
Programming Example – object-oriented approach
var sourceFiles = FolderVisitor.Visit (sourceFolder);
sourceFiles.Sort(Photo.ByDateTaken);
foreach (var sourceFile in sourceFiles) {
try {
FolderHelper.MaterializeFolder(targetFolder, sourceFile.Directory.FullName);
fileCopyCount = 0
while (fileCopyCount < PhotoFileCopyMultiplicationFactor) {
MakeNewTargetFileName
CopySourceFileToTheTargetFolderUnderTheNewName
fileCopyCount += 1
}
}
catch{
}
}
public static class FolderHelper {
public static void CopyFile(string targetRoot,
FileInfo sourceFile, string fileNameBase, int sequenceNo) {
var newFileName = string.Format(„{0}{1}{2}”,
fileNameBase, sequenceNo,
Path.GetExtension(sourceFile.Name));
var realtivePath = sourceFile
.Directory.FullName
.SubString(targetRoot.Length);
var targetPath = Path.Combine(targetRoot, relativePath,
newFileName);
sourceFile.CopyTo(targetPath);
}
}
Make target file
name
Extract target
relative path.
Create target
absolute path.Copy source file to
the target folder.
Programming Example – object-oriented approach
var sourceFiles = FolderVisitor.Visit (sourceFolder);
sourceFiles.Sort(Photo.ByDateTaken);
foreach (var sourceFile in sourceFiles) {
try {
FolderHelper.MaterializeFolder(targetFolder, sourceFile.Directory.FullName);
fileCopyCount = 0
while (fileCopyCount < PhotoFileCopyMultiplicationFactor) {
FolderHelper.CopyFile(targetFolder, sourceFile,
newFileNameBase, sequenceNo);
fileCopyCount += 1;
sequenceNo += 1;
}
}
catch{
}
}
Make a list of source files.Sort source files by the
photo date taken.
Ensure target folder.
Make required file copies.
Update copying process state.
Programming Example – functional approach
• Make the list of source files
• Sort the source file list by picture date taken
• Make the list of pairs - source file and target
folder path
• Make the list of tuples – boolean flag
indicating sucessful file copy, source file,
copy operation status (destination path or
error message)
Programmer focus on what
information is desired and what
transformations are required.
Programming Example – functional approach
• Sort the source file list by picture date taken
• Make the list of pairs - source file and target
folder path
• Make the list of tuples – boolean flag
indicating sucessful file copy, source file,
copy operation status (destination path or
error message)
• Make the list of source filesvar sourceFiles = .Visit (sourceFolder);
type public FileVisitor() =
static member private folderFiles folderPath filter =
let emptyDirectory = List.Empty.AsEnumerable()
let dirInfo = new DirectoryInfo(folderPath)
try
dirInfo.EnumerateFiles(filter)
with
| :? ArgumentException -> emptyDirectory
| :? DirectoryNotFoundException -> emptyDirectory
| :? SecurityException -> emptyDirectory
| :? UnauthorizedAccessException -> emptyDirectory
static member private subFolders folderPath =
let dirInfo = new DirectoryInfo(folderPath)
let noSubFolders = List.Empty.AsEnumerable()
try
dirInfo.EnumerateDirectories()
with
| :? DirectoryNotFoundException -> noSubFolders
| :? SecurityException -> noSubFolders
| :? UnauthorizedAccessException -> noSubFolders
static member public Visit rootFolder fileNameFilter =
seq { yield! FileVisitor.folderFiles rootFolder fileNameFilter
for subFolder in FileVisitor.subFolders rootFolder do
yield! FileVisitor.Visit subFolder.FullName fileNameFilter }
A sequence is a logical series of elements all of one
type. Sequences are particularly useful when you have
a large, ordered collection of data but do not
necessarily expect to use all the elements. Individual
sequence elements are computed only as required, so
a sequence can provide better performance than a list
in situations in which not all the elements are used.
var sourceFiles = FolderVisitor.Visit (sourceFolder);IEnumerable<FileInfo>
Programming Example – functional approach
• Make the list of pairs - source file and target
folder path
• Make the list of tuples – boolean flag
indicating sucessful file copy, source file,
copy operation status (destination path or
error message)
var sourceFiles = FolderVisitor.Visit (sourceFolder);
• Sort the source file list by picture date taken
var sortedFiles = FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken);
IEnumerable<FileInfo>
public static class Photo {
public static DateTime DateTaken(FileInfo f) {
DateTime dateTaken = DateTime.Now;
try {
using (var reader = ExifReader (f.FullName)) {
reader.GetTagValue<DateTime>(
ExifTags.DateTimeDigitized,
our dateTaken);
}
}
catch (IOException) {}
return dateTaken;
}
}
Programming Example – functional approach
• Make the list of pairs - source file and
target folder path
var sortedFiles = FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken);
• Make the list of tuples – boolean flag
indicating sucessful file copy, source file,
copy operation status (destination path or
error message)
var copier =new (targetFolder, baseName, seqStart, noCopies);
var toBeCopiedFiles = FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken)
.SelectMany(copier.MakeTargetPath);
public class FileCopier {
private readonly string _targetFolder;
private readonly string _baseName;
private readonly int _noOfCopies;
private int _sequenceNo;
public FileCopier(string targetFolder, string baseName, int seqStart, int noOfCopies) {
_targetFolder = targetFolder;
_baseName = baseName;
_noOfCopies = noOfCopies;
_sequenceNo = seqStart;
}
public IEnumerable<Tuple<FileInfo, string>> MakeTargetPath(FileInfo file) {
var result = new List<Tuple<FileInfo, string>();
for (var i = 0; i < _noOfCopies; ++i) {
var newFileName = string.Format(„{0}{1}{2}”, _baseName, _sequenceNo,
Path.GetExtension(file.Name));
var realtivePath = file.Directory.FullName.SubString(_targetFolder.Length);
var targetPath = Path.Combine(_targetFolder, relativePath, newFileName);
result.Add(new Tuple<FileInfo, string>(file, targetPath));
_sequenceNo += 1;
}
return result;
}
}
var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies);
var toBeCopiedFiles = FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken)
.SelectMany(copier.MakeTargetPath);
IEnumerable<Tuple<FileInfo, string>>
Projects each element of a sequence to an IEnumerable<T> and
flattens the resulting sequences into one sequence.
public static IEnumerable<TResult> SelectMany<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector
)
Programming Example – functional approach
• Make the list of tuples – boolean flag
indicating sucessful file copy, source file,
copy operation status (destination path or
error message)
var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies);
var toBeCopiedFiles = FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken)
.SelectMany(copier.MakeTargetPath);
var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies);
var fileCopyStatus = FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken)
.SelectMany(copier.MakeTargetPath)
.SelectMany(copier.CopyFile);
public class FileCopier {
private readonly string _targetFolder;
private readonly string _baseName;
private readonly int _noOfCopies;
private int _sequenceNo;
public FileCopier(string targetFolder, string baseName, int seqStart, int noOfCopies) {
_targetFolder = targetFolder;
_baseName = baseName;
_noOfCopies = noOfCopies;
_sequenceNo = seqStart;
}
public IEnumerable<Tuple<bool, FileInfo, string>> CopyFile(Tuple<FileInfo, string> file) {
var result = new List<Tuple<bool, FileInfo, string>();
try {
MaterializeFolder(_targetFolder, file.Item2);
file.Item1.CopyTo(file.Item2);
result.Add(new Tuple<bool, FileInfo, string>(true, file.Item1, file.Item2);
} catch (Exception ex) {
result.Add(new Tuple<bool, FileInfo, string>(false, file.Item1, ex.Message);
}
return result;
}
}
IEnumerable<Tuple<bool, FileInfo, string>>
Programming Example – functional approach
var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies);
var fileCopyStatus = FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken)
.SelectMany(copier.MakeTargetPath)
.SelectMany(copier.CopyFile);
var fileCopyObserver = new ();
fileCopyStatus.Subscribe (fileCopyObserver);
Console.WriteLine(fileCopyObserver.Summary());
public class FileCopyObserver : IObserver<Tuple<bool, FileInfo, string> {
private int _copiedFileCount;
private int _errorCount;
public string Summary() {
return string.Format(„Attempt to copy {0} files; {1} successful; {2} errors.”,
_copiedFileCount + _errorCount, _copiedFileCount, _errorCount);
}
public void OnCompleted() { }
public void OnError (Exception error) {}
public void OnNext(Tuple<bool, FileInfo, string> copyStatus) {
if (copyStatus.Item1) {
_copiedFileCoput += 1;
} else {
_errorCount += 1;
}
}
}
var fileCopyObserver = new FileCopyObserver();
fileCopyStatus.Subscribe (fileCopyObserver);
Console.WriteLine(fileCopyObserver.Summary());
System.Reactive.Linq
Programming Example – Object-Oriented vs Functional approach
var sourceFiles =
FolderVisitor.Visit (sourceFolder);
sourceFiles.Sort(Photo.ByDateTaken);
foreach (var sourceFile in sourceFiles) {
try {
FolderHelper.MaterializeFolder(targetFolder,
sourceFile.Directory.FullName);
fileCopyCount = 0
while (fileCopyCount < noOfCopies) {
FolderHelper.CopyFile(targetFolder,
sourceFile,
newFileNameBase,
sequenceNo);
fileCopyCount += 1;
sequenceNo += 1;
}
}
catch{
}
}
var copier = new FileCopier(targetFolder,
baseName,
seqStart,
noCopies);
var fileCopyStatus =
FolderVisitor.Visit (sourceFolder)
.OrderBy(Photo.DateTaken)
.SelectMany(copier.MakeTargetPath)
.SelectMany(copier.CopyFile);
var fileCopyObserver = new FileCopyObserver();
fileCopyStatus.Subscribe (fileCopyObserver);
Console.WriteLine(fileCopyObserver.Summary());
Control
statements,
state changes Function call
composition
Controlling Complexity By
Controlling Complexity
Composition
Composition is the key to controlling complexity in software.
Expert programmers control the complexity of their designs by
combining primitive elements to form compound objects, they
abstract compound objects to form higher-level building blocks.
One form of composition, function composition, describes the
dependencies between function calls.
Function Composition
Function composition takes two functions and passes the result of
the second function as the argument of the first function –
effectively creating one function taking argument of the second
function and returning result of the first function.
public static Func<T, V> Compose<T, U, V>(this Func<U, V> f, Func<T, U> g)
{
return x => f(g(x));
}
Anonymous function (lambda) calling second function (g) with argument x and
passing its result as first function (f) argument, and returning the result of the
first function.
Function Composition
var r = f(g(x))
•Explicit
dependency
between f and g
var r = f.Compose(g)(x)
•Abstracted
dependency
between f and g
Abstract compound objects to form higher-level building blocks
Function Composition Laws
• Identity.Compose(f) = f
Left
identity
• f.Compose(Identity) = f
Right
identity
• f.Compose(g.Compose(h) =
(f.Compose(g)).Compose(h)Associative
public static T Identity<T>(this T value)
{
return value;
}
Function Composition – sometimes values are not enough
Generic (constructed) types package (amplify) values (integers,
strings, objects of class T)
The type IEnumerable<T> represents a lazily computed list of values
of type T
The type IEnumerable<T> packages (amplifies) the type T
Function Composition with IEnumerable<T>
public static Func<T, IEnumerable<V>> Compose<T, U, V>(
this Func<U, IEnumerable<V>> f,
Func<T, IEnumerable<U>> g)
{
return x => f(g(x));
}
Error – g(x) returns IEnumerable<U> while f takes U
We need to bind the result of g(x) with the function f. Binding will
iterate over a list returned by g(x) and pass individual list items to
the function f. The result of binding g(x) to the function f is a list of
results returned by the function f.
public static Func<T, IEnumerable<V>> Compose<T, U, V>(
this Func<U, IEnumerable<V>> f,
Func<T, IEnumerable<U>> g)
{
return x => g(x).Bind(f);
}
public static IEnumerable<V> Bind (
this IEnumerable<U> m,
Func<U, IEnumerable<V>> f)
public static IEnumerable<V> Bind (this IEnumerable<U> items, Func<U, IEnumerable<V>> f)
{
var result = new List<V>();
foreach (var item in items)
{
result.Add(f(item));
}
return result;
}
Function Composition with IEnumerable<T>
• Our Bind function allows the function f to use IEnumerable returned by the function g
• We need some function converting a type T to IEnumverable<T>
public static IEnumerable<T> Unit (this T value)
• Together the amplified type IEnumerable<T>,
the function Bind, and the function Unit enable
function composition with amplified values
A
type
A Unit
function
A Bind
function
Monad
Monads enable function
composition with amplified values
Monads Satisfy Function Composition Laws
•Unit(v).Bind(f) = f(v)Left identity
•m.Bind(Unit) = mRight identity
•m.Bind(x => k(x).Bind(y => h(y)) =
m.Bind(x =>k(x)).Bind(y => h(y))
Associative
Creating a Monad
To define a particular monad, the writer
supplies the triple (a Type, a Unit function,
and a Bind function), thereby specyfying
the mechanics of the aplified values
IEnumerable<T> Monad
IEnumerable<T> Monad
SelectMany
function
AsEnumerable
function
Type
T
C# Functional
Programming
C# Functional Programming
• Is it worth an effort?
• Maybe it is
• The Maybe monad is an example of a monadic
container type wrapping a value. The container
can either have a value or have missing value.
The Maybe monad is a better nullable type.
C# Functional Programming – Maybe monad
public class Maybe<T> {
public readonly static Maybe<T> Nothing = new Maybe<T>();
public bool IsNothing { get; private set; }
public T Just { get; private set; }
public Maybe() {
IsNothing = true;
Just = default(T);
}
public Maybe(T value) {
IsNothing = false;
Just = value;
}
public new string ToString() {
if (IsNothing) {
return "Nothing";
}
return Just.ToString();
}
}
Nothing represents all instances lacking a value.
Just the value if we have one.
We have the type. Now we need the Unit and
Bind functions to have the Maybe monad.
The Unit function which we call AsMaybe,
wraps a value.
The Bind function which we call SelectMany,
takes a Maybe instance and if there is a value
then it applies the delegate to the contained
value. Otherwise, it returns Nothing.
Maybe monad – Unit and Bind functions
public static class MaybeExtensions {
public static Maybe<T> AsMaybe<T> (this T value){
return new Maybe<T>(value);
}
public static Maybe<U> SelectMany<T, U>(this Maybe<T> m,
Func<T, Maybe<U>> doSemthingWith) {
if (m == null) {
return Maybe<U>.Nothing;
}
if (m.IsNothing) {
return Maybe<U>.Nothing;
}
try {
return doSomethingWith(m.Just);
}
catch (Exception) {
return Maybe<U>.Nothing;
}
}
}
The Unit function which we call AsMaybe, wraps
a value.
The Bind function which we call SelectMany,
takes a Maybe instance and if there is a value
then it applies the delegate to the contained
value. Otherwise, it returns Nothing.
The delegate takes the un-wrapped value of the
type T and returns wrapped (amplified) value of
the type U – we can bind another delegate to
ther result Maybe<U>.
Maybe monad – usage examples
var maybeStringArray = (new string[] {„Ala”, „ma”, „kota”}).AsMaybe();
var maybeResult = maybeStringArray
.SelectMany(x => x.FirstOrDefault(s => s.Equals(„Ala”)).AsMaybe())
.SelectMany(s => s.ToUpperInvariant().AsMaybe());
Console.WriteLine(maybeResult.ToString());
maybeResult = maybeStringArray
.SelectMany(x => x.FirstOrDefault(s => s.Equals(„Ola”)).AsMaybe())
.SelectMany(s => s.ToUpperInvariant().AsMaybe());
Console.WriteLine(maybeResult.ToString());
Writes ALA.
Writes Nothing.
Maybe<string>
Questions

More Related Content

Wiesław Kałkus: C# functional programming

  • 1. WIESŁAW KAŁKUS Holte Software Poland Functional Programming Is it possible in C#?
  • 2. Today’s Agenda C# Functional Programming Is It Worth An Effort? Maybe It Is Controlling Complexity Composition Function Composition Programming Example Copying Photo Files Implementation Variants What Is Functional Programming? Theoretical Perspective C# Programmer Perspective
  • 4. What Is Functional Programming? Wikipedia Functional Programming is a programming paradigm, a style of building the structure and elements of computer programs, that treats computations as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions.
  • 5. Programming Paradigms • Focus on describing how a program operates • Programs described in terms of statements that change a program state • Structural (procedures as main actors) and object-oriented (objects as main actors) programming styles fall into imperative paradigm Imperative • Focus on describing what a program should accomplish without prescribing how to do it in terms of sequences of actions to be taken • Programs expressed in terms of computation logic without describing its control flow • Functional (mathematical function as main actor) programming style falls into declarative paradigm Declarative
  • 6. Multi-paradigm programming languages Type inference (C# & F#) Anonymous functions (C# & F#) Language Integrated Query (C# & F#) Currying (F#) Pattern matching (F#) Lazy evaluation (F#) Tail recursion (F#) Immutability (F#) Control statements (C#) Classes (C#) .NET framework (C# & F#) Do notation (F#) Declarative Imperative
  • 7. Object-Oriented vs. Functional Programming Style Characteristic Object-oriented (imperative) approach Functional (declarative) approach Programmer focus How to perform tasks (algorithms) and how to track changes in state. What information is desired and what transformations are required. State changes Important. Localized and tightly controlled. Order of execution Important. Low importance. Primary flow control Loops, conditionals, and function (method) calls. Function calls, including recursion. Primary manipulation unit Instances of structures or classes. Functions as first-class objects and data collections.
  • 9. Programming Example – problem statement Copy photo files from the source folder (include subfolders) to the destination folder (create subfolders as necessary). While copying rename each file using pattern <base-name><sequence-number>. Sequence numbers should preserve the order of date taken for each photo. Additionally allow for each source photo file to create multiple copies of the file in the target folder (useful for extending time-lapse photography sequences).
  • 10. Programming Example – object-oriented approach foreach (sourceFile in sourceFilesSortedByPhotoDateTaken) { try { CreateTargetFolderPathIfNecessary fileCopyCount = 0 while (fileCopyCount < PhotoFileCopyMultiplicationFactor) { MakeNewTargetFileName CopySourceFileToTheTargetFolderUnderTheNewName fileCopyCount += 1 } } catch { } } Programmer focus on how to perform tasks (algorithms) and how to track changes in state.
  • 11. Programming Example – object-oriented approach foreach (sourceFile in sourceFilesSortedByPhotoDateTaken) { try { CreateTargetFolderPathIfNecessary fileCopyCount = 0 while (fileCopyCount < PhotoFileCopyMultiplicationFactor) { MakeNewTargetFileName CopySourceFileToTheTargetFolderUnderTheNewName fileCopyCount += 1 } } catch { } } var sourceFiles = .Visit (sourceFolder); sourceFiles.Sort( .ByDateTaken); foreach (var sourceFile in sourceFiles) { try { CreateTargetFolderPathIfNecessary fileCopyCount = 0 while (fileCopyCount < PhotoFileCopyMultiplicationFactor) { MakeNewTargetFileName CopySourceFileToTheTargetFolderUnderTheNewName fileCopyCount += 1 } } catch{ } } public static class FolderVisitor { public static List<FileInfo> Visit(string rootFolder) { return DoVisitFolder(rootFolder); } private static List<FileInfo> DoVisitFolder(string folder) { var files = new List<FileInfo>(); var thisFolder = new DirectoryInfo(folder); files.Add(thisFolder.EnumerateFiles()); var subFolders = thisFolder.EnumerateFolders(); foreach (var folder in subFolders) { files.Add(DoVisitFolder(subfolder.FullName)); } return files; } } public static class Photo { public static int ByDateTaken (FileInfo f1, FileInfo f2) { if (f1 == null && f2 == null) { return 0; } else if (f1 == null) { return -1; } else if (f2 == null) { return 1; } return Photo.DateTaken(f1).CompareTo(Photo.DateTaken(f1)); } private static DateTime DateTaken(FileInfo f) { DateTime dateTaken = DateTime.Now; try { using (var reader = ExifReader (f.FullName)) { reader.GetTagValue<DateTime>( ExifTags.DateTimeDigitized, our dateTaken); } } catch (IOException) {} return dateTaken; } } Add files from the current folder. Recursively add files from subfolders. Open source Exif Library – read EXIF tag from the photo file.
  • 12. Programming Example – object-oriented approach var sourceFiles = FolderVisitor.Visit (sourceFolder); sourceFiles.Sort(Photo.ByDateTaken); foreach (var sourceFile in sourceFiles) { try { CreateTargetFolderPathIfNecessary fileCopyCount = 0 while (fileCopyCount < PhotoFileCopyMultiplicationFactor) { MakeNewTargetFileName CopySourceFileToTheTargetFolderUnderTheNewName fileCopyCount += 1 } } catch{ } } public static class FolderHelper { public static void MaterializeFolder(string root string path) { var realtivePath = path.SubString(root.Length); if (string.IsNullOrWhitespace(relativePath)) { return; } var segments = relativePath.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); var workingPath = root; foreach (var segment in segments) { workingPath = Path.Combine(workingPath, segment); if (!Directory.Exists(workingPath)) { Directory.CreateDirectory(workingPath); } } } } Create missing folders.
  • 13. Programming Example – object-oriented approach var sourceFiles = FolderVisitor.Visit (sourceFolder); sourceFiles.Sort(Photo.ByDateTaken); foreach (var sourceFile in sourceFiles) { try { FolderHelper.MaterializeFolder(targetFolder, sourceFile.Directory.FullName); fileCopyCount = 0 while (fileCopyCount < PhotoFileCopyMultiplicationFactor) { MakeNewTargetFileName CopySourceFileToTheTargetFolderUnderTheNewName fileCopyCount += 1 } } catch{ } } public static class FolderHelper { public static void CopyFile(string targetRoot, FileInfo sourceFile, string fileNameBase, int sequenceNo) { var newFileName = string.Format(„{0}{1}{2}”, fileNameBase, sequenceNo, Path.GetExtension(sourceFile.Name)); var realtivePath = sourceFile .Directory.FullName .SubString(targetRoot.Length); var targetPath = Path.Combine(targetRoot, relativePath, newFileName); sourceFile.CopyTo(targetPath); } } Make target file name Extract target relative path. Create target absolute path.Copy source file to the target folder.
  • 14. Programming Example – object-oriented approach var sourceFiles = FolderVisitor.Visit (sourceFolder); sourceFiles.Sort(Photo.ByDateTaken); foreach (var sourceFile in sourceFiles) { try { FolderHelper.MaterializeFolder(targetFolder, sourceFile.Directory.FullName); fileCopyCount = 0 while (fileCopyCount < PhotoFileCopyMultiplicationFactor) { FolderHelper.CopyFile(targetFolder, sourceFile, newFileNameBase, sequenceNo); fileCopyCount += 1; sequenceNo += 1; } } catch{ } } Make a list of source files.Sort source files by the photo date taken. Ensure target folder. Make required file copies. Update copying process state.
  • 15. Programming Example – functional approach • Make the list of source files • Sort the source file list by picture date taken • Make the list of pairs - source file and target folder path • Make the list of tuples – boolean flag indicating sucessful file copy, source file, copy operation status (destination path or error message) Programmer focus on what information is desired and what transformations are required.
  • 16. Programming Example – functional approach • Sort the source file list by picture date taken • Make the list of pairs - source file and target folder path • Make the list of tuples – boolean flag indicating sucessful file copy, source file, copy operation status (destination path or error message) • Make the list of source filesvar sourceFiles = .Visit (sourceFolder); type public FileVisitor() = static member private folderFiles folderPath filter = let emptyDirectory = List.Empty.AsEnumerable() let dirInfo = new DirectoryInfo(folderPath) try dirInfo.EnumerateFiles(filter) with | :? ArgumentException -> emptyDirectory | :? DirectoryNotFoundException -> emptyDirectory | :? SecurityException -> emptyDirectory | :? UnauthorizedAccessException -> emptyDirectory static member private subFolders folderPath = let dirInfo = new DirectoryInfo(folderPath) let noSubFolders = List.Empty.AsEnumerable() try dirInfo.EnumerateDirectories() with | :? DirectoryNotFoundException -> noSubFolders | :? SecurityException -> noSubFolders | :? UnauthorizedAccessException -> noSubFolders static member public Visit rootFolder fileNameFilter = seq { yield! FileVisitor.folderFiles rootFolder fileNameFilter for subFolder in FileVisitor.subFolders rootFolder do yield! FileVisitor.Visit subFolder.FullName fileNameFilter } A sequence is a logical series of elements all of one type. Sequences are particularly useful when you have a large, ordered collection of data but do not necessarily expect to use all the elements. Individual sequence elements are computed only as required, so a sequence can provide better performance than a list in situations in which not all the elements are used. var sourceFiles = FolderVisitor.Visit (sourceFolder);IEnumerable<FileInfo>
  • 17. Programming Example – functional approach • Make the list of pairs - source file and target folder path • Make the list of tuples – boolean flag indicating sucessful file copy, source file, copy operation status (destination path or error message) var sourceFiles = FolderVisitor.Visit (sourceFolder); • Sort the source file list by picture date taken var sortedFiles = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken); IEnumerable<FileInfo> public static class Photo { public static DateTime DateTaken(FileInfo f) { DateTime dateTaken = DateTime.Now; try { using (var reader = ExifReader (f.FullName)) { reader.GetTagValue<DateTime>( ExifTags.DateTimeDigitized, our dateTaken); } } catch (IOException) {} return dateTaken; } }
  • 18. Programming Example – functional approach • Make the list of pairs - source file and target folder path var sortedFiles = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken); • Make the list of tuples – boolean flag indicating sucessful file copy, source file, copy operation status (destination path or error message) var copier =new (targetFolder, baseName, seqStart, noCopies); var toBeCopiedFiles = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken) .SelectMany(copier.MakeTargetPath); public class FileCopier { private readonly string _targetFolder; private readonly string _baseName; private readonly int _noOfCopies; private int _sequenceNo; public FileCopier(string targetFolder, string baseName, int seqStart, int noOfCopies) { _targetFolder = targetFolder; _baseName = baseName; _noOfCopies = noOfCopies; _sequenceNo = seqStart; } public IEnumerable<Tuple<FileInfo, string>> MakeTargetPath(FileInfo file) { var result = new List<Tuple<FileInfo, string>(); for (var i = 0; i < _noOfCopies; ++i) { var newFileName = string.Format(„{0}{1}{2}”, _baseName, _sequenceNo, Path.GetExtension(file.Name)); var realtivePath = file.Directory.FullName.SubString(_targetFolder.Length); var targetPath = Path.Combine(_targetFolder, relativePath, newFileName); result.Add(new Tuple<FileInfo, string>(file, targetPath)); _sequenceNo += 1; } return result; } } var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies); var toBeCopiedFiles = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken) .SelectMany(copier.MakeTargetPath); IEnumerable<Tuple<FileInfo, string>> Projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences into one sequence. public static IEnumerable<TResult> SelectMany<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector )
  • 19. Programming Example – functional approach • Make the list of tuples – boolean flag indicating sucessful file copy, source file, copy operation status (destination path or error message) var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies); var toBeCopiedFiles = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken) .SelectMany(copier.MakeTargetPath); var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies); var fileCopyStatus = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken) .SelectMany(copier.MakeTargetPath) .SelectMany(copier.CopyFile); public class FileCopier { private readonly string _targetFolder; private readonly string _baseName; private readonly int _noOfCopies; private int _sequenceNo; public FileCopier(string targetFolder, string baseName, int seqStart, int noOfCopies) { _targetFolder = targetFolder; _baseName = baseName; _noOfCopies = noOfCopies; _sequenceNo = seqStart; } public IEnumerable<Tuple<bool, FileInfo, string>> CopyFile(Tuple<FileInfo, string> file) { var result = new List<Tuple<bool, FileInfo, string>(); try { MaterializeFolder(_targetFolder, file.Item2); file.Item1.CopyTo(file.Item2); result.Add(new Tuple<bool, FileInfo, string>(true, file.Item1, file.Item2); } catch (Exception ex) { result.Add(new Tuple<bool, FileInfo, string>(false, file.Item1, ex.Message); } return result; } } IEnumerable<Tuple<bool, FileInfo, string>>
  • 20. Programming Example – functional approach var copier =new FileCopier(targetFolder, baseName, seqStart, noCopies); var fileCopyStatus = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken) .SelectMany(copier.MakeTargetPath) .SelectMany(copier.CopyFile); var fileCopyObserver = new (); fileCopyStatus.Subscribe (fileCopyObserver); Console.WriteLine(fileCopyObserver.Summary()); public class FileCopyObserver : IObserver<Tuple<bool, FileInfo, string> { private int _copiedFileCount; private int _errorCount; public string Summary() { return string.Format(„Attempt to copy {0} files; {1} successful; {2} errors.”, _copiedFileCount + _errorCount, _copiedFileCount, _errorCount); } public void OnCompleted() { } public void OnError (Exception error) {} public void OnNext(Tuple<bool, FileInfo, string> copyStatus) { if (copyStatus.Item1) { _copiedFileCoput += 1; } else { _errorCount += 1; } } } var fileCopyObserver = new FileCopyObserver(); fileCopyStatus.Subscribe (fileCopyObserver); Console.WriteLine(fileCopyObserver.Summary()); System.Reactive.Linq
  • 21. Programming Example – Object-Oriented vs Functional approach var sourceFiles = FolderVisitor.Visit (sourceFolder); sourceFiles.Sort(Photo.ByDateTaken); foreach (var sourceFile in sourceFiles) { try { FolderHelper.MaterializeFolder(targetFolder, sourceFile.Directory.FullName); fileCopyCount = 0 while (fileCopyCount < noOfCopies) { FolderHelper.CopyFile(targetFolder, sourceFile, newFileNameBase, sequenceNo); fileCopyCount += 1; sequenceNo += 1; } } catch{ } } var copier = new FileCopier(targetFolder, baseName, seqStart, noCopies); var fileCopyStatus = FolderVisitor.Visit (sourceFolder) .OrderBy(Photo.DateTaken) .SelectMany(copier.MakeTargetPath) .SelectMany(copier.CopyFile); var fileCopyObserver = new FileCopyObserver(); fileCopyStatus.Subscribe (fileCopyObserver); Console.WriteLine(fileCopyObserver.Summary()); Control statements, state changes Function call composition Controlling Complexity By
  • 23. Composition Composition is the key to controlling complexity in software. Expert programmers control the complexity of their designs by combining primitive elements to form compound objects, they abstract compound objects to form higher-level building blocks. One form of composition, function composition, describes the dependencies between function calls.
  • 24. Function Composition Function composition takes two functions and passes the result of the second function as the argument of the first function – effectively creating one function taking argument of the second function and returning result of the first function. public static Func<T, V> Compose<T, U, V>(this Func<U, V> f, Func<T, U> g) { return x => f(g(x)); } Anonymous function (lambda) calling second function (g) with argument x and passing its result as first function (f) argument, and returning the result of the first function.
  • 25. Function Composition var r = f(g(x)) •Explicit dependency between f and g var r = f.Compose(g)(x) •Abstracted dependency between f and g Abstract compound objects to form higher-level building blocks
  • 26. Function Composition Laws • Identity.Compose(f) = f Left identity • f.Compose(Identity) = f Right identity • f.Compose(g.Compose(h) = (f.Compose(g)).Compose(h)Associative public static T Identity<T>(this T value) { return value; }
  • 27. Function Composition – sometimes values are not enough Generic (constructed) types package (amplify) values (integers, strings, objects of class T) The type IEnumerable<T> represents a lazily computed list of values of type T The type IEnumerable<T> packages (amplifies) the type T
  • 28. Function Composition with IEnumerable<T> public static Func<T, IEnumerable<V>> Compose<T, U, V>( this Func<U, IEnumerable<V>> f, Func<T, IEnumerable<U>> g) { return x => f(g(x)); } Error – g(x) returns IEnumerable<U> while f takes U We need to bind the result of g(x) with the function f. Binding will iterate over a list returned by g(x) and pass individual list items to the function f. The result of binding g(x) to the function f is a list of results returned by the function f. public static Func<T, IEnumerable<V>> Compose<T, U, V>( this Func<U, IEnumerable<V>> f, Func<T, IEnumerable<U>> g) { return x => g(x).Bind(f); } public static IEnumerable<V> Bind ( this IEnumerable<U> m, Func<U, IEnumerable<V>> f) public static IEnumerable<V> Bind (this IEnumerable<U> items, Func<U, IEnumerable<V>> f) { var result = new List<V>(); foreach (var item in items) { result.Add(f(item)); } return result; }
  • 29. Function Composition with IEnumerable<T> • Our Bind function allows the function f to use IEnumerable returned by the function g • We need some function converting a type T to IEnumverable<T> public static IEnumerable<T> Unit (this T value) • Together the amplified type IEnumerable<T>, the function Bind, and the function Unit enable function composition with amplified values A type A Unit function A Bind function Monad Monads enable function composition with amplified values
  • 30. Monads Satisfy Function Composition Laws •Unit(v).Bind(f) = f(v)Left identity •m.Bind(Unit) = mRight identity •m.Bind(x => k(x).Bind(y => h(y)) = m.Bind(x =>k(x)).Bind(y => h(y)) Associative
  • 31. Creating a Monad To define a particular monad, the writer supplies the triple (a Type, a Unit function, and a Bind function), thereby specyfying the mechanics of the aplified values
  • 34. C# Functional Programming • Is it worth an effort? • Maybe it is • The Maybe monad is an example of a monadic container type wrapping a value. The container can either have a value or have missing value. The Maybe monad is a better nullable type.
  • 35. C# Functional Programming – Maybe monad public class Maybe<T> { public readonly static Maybe<T> Nothing = new Maybe<T>(); public bool IsNothing { get; private set; } public T Just { get; private set; } public Maybe() { IsNothing = true; Just = default(T); } public Maybe(T value) { IsNothing = false; Just = value; } public new string ToString() { if (IsNothing) { return "Nothing"; } return Just.ToString(); } } Nothing represents all instances lacking a value. Just the value if we have one. We have the type. Now we need the Unit and Bind functions to have the Maybe monad. The Unit function which we call AsMaybe, wraps a value. The Bind function which we call SelectMany, takes a Maybe instance and if there is a value then it applies the delegate to the contained value. Otherwise, it returns Nothing.
  • 36. Maybe monad – Unit and Bind functions public static class MaybeExtensions { public static Maybe<T> AsMaybe<T> (this T value){ return new Maybe<T>(value); } public static Maybe<U> SelectMany<T, U>(this Maybe<T> m, Func<T, Maybe<U>> doSemthingWith) { if (m == null) { return Maybe<U>.Nothing; } if (m.IsNothing) { return Maybe<U>.Nothing; } try { return doSomethingWith(m.Just); } catch (Exception) { return Maybe<U>.Nothing; } } } The Unit function which we call AsMaybe, wraps a value. The Bind function which we call SelectMany, takes a Maybe instance and if there is a value then it applies the delegate to the contained value. Otherwise, it returns Nothing. The delegate takes the un-wrapped value of the type T and returns wrapped (amplified) value of the type U – we can bind another delegate to ther result Maybe<U>.
  • 37. Maybe monad – usage examples var maybeStringArray = (new string[] {„Ala”, „ma”, „kota”}).AsMaybe(); var maybeResult = maybeStringArray .SelectMany(x => x.FirstOrDefault(s => s.Equals(„Ala”)).AsMaybe()) .SelectMany(s => s.ToUpperInvariant().AsMaybe()); Console.WriteLine(maybeResult.ToString()); maybeResult = maybeStringArray .SelectMany(x => x.FirstOrDefault(s => s.Equals(„Ola”)).AsMaybe()) .SelectMany(s => s.ToUpperInvariant().AsMaybe()); Console.WriteLine(maybeResult.ToString()); Writes ALA. Writes Nothing. Maybe<string>

Editor's Notes

  1. Kompozycja, w sensie składania mniejszych elementów w większe konstrukcje jest kluczem do kontroli złożoności oprogramowania. Zawodowcy kontrolują złożoność swoich projektów poprzez składanie prostych elementów w złożone obiekty. Abstrahowanie pozwala przekształcić złożone obiekty w klocki wyższego poziomu obdarzone funkcjonalnością umożliwiającą budowanie skomplikowanych systemów z dobrze zdefiniowanych składników (którymi łatwo się można posługiwać, bo są inteligetne tj. zawierają tyle funkcjonalności, że łatwo się jest składa w większą całość). Jedną z form kompozycji (składania) jest złożenie funkcji.
  2. Compose – funkcja zwracająca funkcję – co za pomysł! Na dodatek to extension method?! I to jeszcze generic! A dlaczego nie?
  3. Associative - przechodni
  4. Wróćmy do naszej funkcji Compose i zamieńmy parametr typu V na parametr typu IEnumerable<V>. Czy taka podmiana nadal umożliwia składanie funkcji? Dobrze nam szło ale się skończyło. Jak zaradzić tej drobnej przeszkodzie?
  5. Podsumujmy to co do tej pory uzyskaliśmy. Bind umożliwia funkcji f konsumpcję wyniku zwracanego przez funkcję g Przydałaby się funkcja, która wzmacnia dowolny typ T to IEnumerable<T> - nazwijmy ją Unit – pomińmy na moment jej implementację Co daje nam ta trójka (IEnumerable<T>, Bind i Unit)? Możliwość złożenia funkcji biorących argumenty wzmocnionych typów i zwracających wyniki w wzmocnionych typach. Taka trójka ma swoją nazwę – poznajcie Monadę – tajemniczy termin ze słownictwa programowania funkcjonalnego