C# Language
Programación Funcional
Buscar..
Func y Acción
Func proporciona un soporte para funciones anónimas parametrizadas. Los tipos principales son las entradas y el último tipo es siempre el valor de retorno.
// square a number.
Func<double, double> square = (x) => { return x * x; };
// get the square root.
// note how the signature matches the built in method.
Func<double, double> squareroot = Math.Sqrt;
// provide your workings.
Func<double, double, string> workings = (x, y) =>
string.Format("The square of {0} is {1}.", x, square(y))
Los objetos de acción son como métodos de vacío, por lo que solo tienen un tipo de entrada. No se coloca ningún resultado en la pila de evaluación.
// right-angled triangle.
class Triangle
{
public double a;
public double b;
public double h;
}
// Pythagorean theorem.
Action<Triangle> pythagoras = (x) =>
x.h = squareroot(square(x.a) + square(x.b));
Triangle t = new Triangle { a = 3, b = 4 };
pythagoras(t);
Console.WriteLine(t.h); // 5.
Inmutabilidad
La inmutabilidad es común en la programación funcional y rara en la programación orientada a objetos.
Cree, por ejemplo, un tipo de dirección con estado mutable:
public class Address ()
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
}
Cualquier pieza de código podría alterar cualquier propiedad en el objeto anterior.
Ahora crea el tipo de dirección inmutable:
public class Address ()
{
public readonly string Line1;
public readonly string Line2;
public readonly string City;
public Address(string line1, string line2, string city)
{
Line1 = line1;
Line2 = line2;
City = city;
}
}
Tenga en cuenta que tener colecciones de solo lectura no respeta la inmutabilidad. Por ejemplo,
public class Classroom
{
public readonly List<Student> Students;
public Classroom(List<Student> students)
{
Students = students;
}
}
no es inmutable, ya que el usuario del objeto puede alterar la colección (agregar o quitar elementos de ella). Para hacerlo inmutable, uno tiene que usar una interfaz como IEnumerable, que no expone métodos para agregar, o hacer que sea ReadOnlyCollection.
public class Classroom
{
public readonly ReadOnlyCollection<Student> Students;
public Classroom(ReadOnlyCollection<Student> students)
{
Students = students;
}
}
List<Students> list = new List<Student>();
// add students
Classroom c = new Classroom(list.AsReadOnly());
Con el objeto inmutable tenemos los siguientes beneficios:
- Estará en un estado conocido (otro código no puede cambiarlo).
- Es hilo seguro.
- El constructor ofrece un solo lugar para la validación.
- Saber que el objeto no puede ser alterado hace que el código sea más fácil de entender.
Evitar referencias nulas
Los desarrolladores de C # obtienen muchas excepciones de referencia nulas con las que lidiar. Los desarrolladores de F # no lo hacen porque tienen el tipo de opción. Un tipo Opción <> (algunos prefieren Maybe <> como nombre) proporciona un tipo de retorno Algunos y Ninguno. Hace explícito que un método puede estar a punto de devolver un registro nulo.
Por ejemplo, no puede leer lo siguiente y saber si tendrá que lidiar con un valor nulo.
var user = _repository.GetUser(id);
Si conoce el posible nulo, puede introducir algún código repetitivo para resolverlo.
var username = user != null ? user.Name : string.Empty;
¿Qué pasa si tenemos una opción <> devuelta en su lugar?
Option<User> maybeUser = _repository.GetUser(id);
Ahora el código hace explícito que es posible que se nos devuelva un registro Ninguno y que se requiera el código repetitivo para verificar Algunos o Ninguno:
var username = maybeUser.HasValue ? maybeUser.Value.Name : string.Empty;
El siguiente método muestra cómo devolver una Opción <>
public Option<User> GetUser(int id)
{
var users = new List<User>
{
new User { Id = 1, Name = "Joe Bloggs" },
new User { Id = 2, Name = "John Smith" }
};
var user = users.FirstOrDefault(user => user.Id == id);
return user != null ? new Option<User>(user) : new Option<User>();
}
Aquí hay una implementación mínima de la Opción <>.
public struct Option<T>
{
private readonly T _value;
public T Value
{
get
{
if (!HasValue)
throw new InvalidOperationException();
return _value;
}
}
public bool HasValue
{
get { return _value != null; }
}
public Option(T value)
{
_value = value;
}
public static implicit operator Option<T>(T value)
{
return new Option<T>(value);
}
}
Para demostrar lo anterior, AvoidNull.csx puede ejecutarse con C # REPL.
Como se dijo, esta es una implementación mínima. Una búsqueda de "Tal vez" paquetes NuGet mostrará una serie de buenas bibliotecas.
Funciones de orden superior
Una función de orden superior es aquella que toma otra función como argumento o devuelve una función (o ambas).
Esto se hace comúnmente con lambdas, por ejemplo, al pasar un predicado a una cláusula LINQ Where:
var results = data.Where(p => p.Items == 0);
La cláusula Where () podría recibir muchos predicados diferentes, lo que le da una flexibilidad considerable.
Al implementar el patrón de diseño de la Estrategia, también se ve pasar un método a otro. Por ejemplo, se podrían elegir varios métodos de clasificación y pasar a un método de Clasificación en un objeto, dependiendo de los requisitos en tiempo de ejecución.
Colecciones inmutables
El paquete System.Collections.Immutable
NuGet proporciona clases de colección inmutables.
Creación y adición de elementos
var stack = ImmutableStack.Create<int>();
var stack2 = stack.Push(1); // stack is still empty, stack2 contains 1
var stack3 = stack.Push(2); // stack2 still contains only one, stack3 has 2, 1
Creando usando el constructor
Ciertas colecciones inmutables tienen una clase interna de Builder
que se puede usar para construir grandes instancias inmutables a bajo costo:
var builder = ImmutableList.CreateBuilder<int>(); // returns ImmutableList.Builder
builder.Add(1);
builder.Add(2);
var list = builder.ToImmutable();
Creando desde un IEnumerable existente
var numbers = Enumerable.Range(1, 5);
var list = ImmutableList.CreateRange<int>(numbers);
Lista de todos los tipos de colecciones inmutables:
-
System.Collections.Immutable.ImmutableArray<T>
-
System.Collections.Immutable.ImmutableDictionary<TKey,TValue>
-
System.Collections.Immutable.ImmutableHashSet<T>
-
System.Collections.Immutable.ImmutableList<T>
-
System.Collections.Immutable.ImmutableQueue<T>
-
System.Collections.Immutable.ImmutableSortedDictionary<TKey,TValue>
-
System.Collections.Immutable.ImmutableSortedSet<T>
-
System.Collections.Immutable.ImmutableStack<T>