Programming is hard, and humans are bad at it
class or method with a Type parameter in angle brackets
Consuming:
Type parameter after the method name.
public List<T> Contain<T>(T a, T b)
{
var result = new List<T>();
result.Add(a);
result.Add(b);
return result;
}
Call the type whatever you want.
Can have multiple types, separated by commas.
Every instance of the type name refers to that type.
The entire class is parameterized by the type.
Put the type after the class name.
public class ConfigManager<T> where T : class
{
private T config;
public T GetConfig() { ... }
private void LoadConfig() { ... }
}
Action and Func are types used to refer to some method or delegate.
Action's return type is void
Func returns its last type parameter
example:
Func<string, int> takes a string and returns an int
Action<bool> takes a bool and returns void
public bool IsHappy(Person p) { return p.Happiness > 75; } Func<Person, bool> happyTester = IsHappy;
happyTester(bob);
A lambda is a shortcut for making code without making a method.
Func<int, bool> isAdult = (int age) =>
{
return age > 17;
};
equivalent:
Func<int, bool> isAdult = age => age > 17;
A closure is any lambda/delegate which "closes over" or "captures" things in its scope.
var limit = 0.04;
Func<float, bool> aboveLimit = bac => bac > limit;
limit is a local variable, but aboveLimit captures it for its own use.
Even if limit goes out of scope, aboveLimit can still use it.
Closures in C# are by reference, so the captured values can be changed and those changes will be reflected.
programming by composing pure functions
both:
style
informed by language
a "pure" function is a map of inputs to outputs
public void DoubleNumbers(int[] list)
{
for (int i = 0; i < list.Length; i++)
{
list[i] = list[i] * 2;
}
}
Can I do anything at all?
You can compute values and return them.
immutability: objects cannot change
Have you ever tried to do this:
string firstName = firstNameField.Text;
firstName.ToLower(); //this does nothing
functions as variables / values
public IList<int> YearsUntilDeath(IList<Person> people) { Func<Person, int> death = (Person p) =>
{
return 70 - p.Age;
};
return people.Select(x => death(x)).ToList(); }
pass a function as an argument
public string DecodeMessage(string message, Func<string, string> decoder)
{
return decoder(message);
}
public Func<int, int> GetEmbiggener()
{
Func<int, int> embiggener = (int x) =>
{
return x * 100;
};
return embiggener;
}
fancy name for a function that either:
students.Select(x => x.Name);
IEnumerable<U> Select<T,U>(this IEnumerable<T> source, Func<T,U> map)
Immutability means you can't change things.
Not even a loop index: i++
Then how do you loop through an array?
Recursion.
public int Max(int[] nums) {
if(nums.Length < 2) return nums.Head; var maxTail = Max(nums.Tail); return maxTail > nums.Head ? maxTail : nums.Head; }
don't bother
not practical in C#
practice pragmatism
C# has local type inference
var x = 10;
functional programming languages usually do better
var list = [];
var person = new Person();
list.Add(person);
function Add(a, b) {
a + b
}
two or more values
public Tuple<string, Gender, FavoriteColor> WhoAreYou()
{
return Tuple.Create("Ross", Gender.Male, Color.All);
}
public bool IsMoreThanTen(int n)
{
if (n > 10)
true
else
false
}
discriminated union / tagged union
type Shape {
Circle(Point, float),
Rectangle(Point, Point)
}
public float Area(Shape sh) {
match sh {
Circle(_, radius) => PI * radius * radius,
Rectangle(Point{x,y}, Point{x2,y2}) => (x2 - x) * (y2 - y)
}
}
programming by composing pure functions
in a declarative style
often using immutable values
usually with a rich type system
simple things that compose well
easier to write reusable code
promotes correct code
especially effective in the small scale
promotes readability and testability
simple vs easy
complex:
com - "with"
plectere - "to weave, braid, entwine"
Try to avoid:
Functional programming doesn't always make sense
Make a judgment call
Incorporate it slowly into your toolbox
Works best when computing values or shuffling data around
Works poorly when accessing resources:
Pure functional programming is not practical in C#
Do what you can and glue the rest
Bind signature:
M<TResult> Bind<TSource, TResult>(M<TSource> m, Func<TSource, M<TResult>> f)
Bind lets you operate on a wrapped T by passing a function, while the monad takes care of whatever logic it abstracts.
Examples in C#: