Compartir a través de


Conceptos básicos de F#

Introducción a programación funcional para desarrolladores de .NET

Chris Marinos

Descargar el ejemplo de código

Es muy probable que ya haya escuchado acerca de F#, la adición más reciente a la familia de lenguajes Visual Studio de Microsoft. Existen muchas razones interesantes para aprender F#: tiene una sintaxis limpia, capacidades de subprocesamiento múltiple eficaces e interoperabilidad fluida con otros lenguajes de Microsoft .NET Framework. Sin embargo, F# también incluye algunos conceptos importantes nuevos que será necesario que comprenda antes de poder aprovechar estas características.

Un paseo rápido es una buena forma de comenzar a aprender otro lenguaje orientado hacia objetos, o incluso un lenguaje dinámico como Ruby o Python. Esto se debe a que usted ya conoce la mayoría del vocabulario y sólo necesita aprender la nueva sintaxis. F#, sin embargo, es diferente. F# es un lenguaje de programación funcional, lo que implica más vocabulario nuevo del que usted pudiera esperar. Además, los lenguajes funcionales tradicionalmente se han usado en círculos académicos, de modo que las definiciones de estos nuevos términos pueden ser difíciles de entender.

Afortunadamente, F# no está diseñado para ser un lenguaje académico. Su sintaxis permite usar técnicas funcionales para resolver problemas de nuevas y mejores formas y, al mismo tiempo, sigue siendo compatible con los estilos orientados hacia objetos e imperativos a los que usted está acostumbrado como desarrollador de .NET. A diferencia de otros lenguajes .NET, la estructura de paradigmas múltiples que F# significa que usted es libre de elegir el mejor estilo de programación para el problema que intenta resolver. La programación funcional en F# consiste en escritura de código conciso y eficaz para resolver problemas prácticos de software. Consiste en el uso de técnicas, como funciones de orden mayor y composición de función, para crear comportamientos eficaces y fáciles de entender. También consiste en hacer que el código sea más fácil de entender, probar y poner en paralelo, lo que elimina las complejidades ocultas.

Sin embargo, para poder aprovechar todas estas fantásticas características de F#, es necesario comprender los conceptos básicos. En este artículo, explicaré estos conceptos mediante vocabulario con el que usted ya está familiarizado como desarrollador de .NET. También le mostraré algunas de las técnicas de programación funcional que puede aplicar a su código existente y algunas de las formas en que ya programa funcionalmente. Al final del artículo, usted conocerá bastante acerca de la programación funcional, de modo que podrá ganar terreno en la ejecución de F# en Visual Studio 2010.

Conceptos básicos de la programación funcional

Para la mayoría de los desarrolladores de .NET, resulta más fácil comprender lo que es la programación funcional al entender lo que no es. La programación imperativa es un estilo de programación que se considera opuesta a la programación funcional. También es el estilo de programación con el que probablemente ya esté más familiarizado, dado que la mayor parte de los lenguajes de programación estándar son imperativos.

La programación funcional y la programación imperativa se diferencian en un nivel muy fundamental y usted puede observarlo hasta en el código más simple:

int number = 0;
number++;

Esto obviamente aumenta una variable en uno. Eso no es muy fascinante, pero considere una manera diferente en que podría resolver el problema:

const int number = 0;
const int result = number + 1;

number aún se aumenta en uno, pero no cambia de lugar. En vez de eso, el resultado se almacena como otra constante, dado que el compilador no permite modificar el valor de una constante. Se puede decir que las constantes son inmutables debido a que el valor no se puede cambiar una vez definido. A la inversa, la variable number de mi primer ejemplo era mutable, ya que se puede modificar su valor. Estos dos enfoques muestran una de las diferencias fundamentales entre programación imperativa y programación funcional. La programación imperativa pone énfasis en el uso de variables mutables, mientras que la programación funcional usa valores inmutables.

La mayoría de los desarrolladores de .NET indicaría que number y result del ejemplo anterior son variables, pero como programador funcional, se debe ser más cuidadoso. Después de todo, la idea de una variable constante es, en el mejor de los casos, confusa. En su lugar, los programadores funcionales opinan que number y result son valores. Asegúrese de reservar el término “variable” para objetos que sean mutables. Tenga en cuenta que estos términos no son exclusivos de la programación funcional, pero son mucho más importantes al programar en estilo funcional.

Esta distinción puede parecer pequeña, pero es la base para muchos de los conceptos que hacen que la programación funcional sea tan eficaz. Las variables mutables son la causa fundamental de muchos errores desagradables. Como verá a continuación, conducen a dependencias implícitas entre diferentes partes del código, lo que produce muchos problemas, especialmente relacionados con simultaneidad. Por el contrario, las variables inmutables presentan significativamente menos complejidad. Conducen a técnicas funcionales como el uso de funciones como valores y la programación compositiva, que también exploraré en más detalle más adelante.

Si usted es escéptico acerca de la programación funcional hasta ahora, no se preocupe. Es natural. La mayoría de los programadores imperativos están entrenados para creer que es imposible hacer algo útil con valores inmutables. Sin embargo, considere este ejemplo:

string stringValue = "world!";
string result = stringValue.Insert(0, "hello ");

La función Insert compiló la cadena “hello world!”, pero usted sabe que Insert no modifica el valor de la cadena de origen. Esto se debe a que las cadenas son inmutables en .NET. Los diseñadores de .NET Framework usaron un enfoque funcional, porque facilitaba escribir mejor código con cadenas. Dado que las cadenas son uno de los tipos de datos que se usan más ampliamente en .NET Framework (junto con otros tipos base, como enteros, DateTimes, etc.), es muy probable que ya haya realizado programación funcional y más útil sin darse cuenta.

Poner F# en funcionamiento

F# viene incluido con Visual Studio 2010 y usted puede buscar la versión más reciente en msdn.microsoft.com/vstudio. Si usa Visual Studio 2008, puede descargar un complemento de F# del Centro de desarrolladores de F# en msdn.microsoft.com/fsharp, donde encontrará instrucciones de instalación para Mono.

F# agrega una nueva ventana a Visual Studio llamada F# Interactive que, como su nombre en inglés lo indica, permite ejecutar código F# interactivamente. Puede considerarla una versión más eficaz de la ventana Immediate, a la que puede tener acceso aunque no se encuentre en modo de depuración. Si está familiarizado con Ruby o Python, reconocerá que F# Interactive es un bucle Read-Evaluate-Print (REPL), una herramienta útil para aprender F# y experimentar con código rápidamente.

Usaré F# Interactive en este artículo para demostrar lo que sucede cuando se compila y ejecuta código de ejemplo. Si se resalta código en Visual Studio y se presiona Alt+Intro, el código se envía a F# Interactive. Para ver esto, el siguiente es un ejemplo de adición simple en F#:

let number = 0
let result = number + 1

Cuando se ejecuta este código en F# Interactive, se obtiene lo siguiente:

val number : int = 0
val result : int = 1

Probablemente deduzca por el término val que number y result son valores inmutables, no variables mutables. Puede ver esto mediante <-, el operador de asignación de F#:

> number <- 15;;
  number <- 15;;
  ^^^^^^^^^^^^
stdin(3,1): error FS0027: This value is not mutable
>

Dado que usted sabe que la programación funcional se basa en la inmutabilidad, este error debe tener sentido. La palabra clave let se usa para crear enlaces inmutables entre nombres y valores. En términos de C#, todo es const de manera predeterminada en F#. Puede realizar una variable mutable si lo desea, pero debe indicarlo explícitamente. Los valores predeterminados sólo son lo opuesto de aquello con lo que está familiarizado en los lenguajes imperativos:

let mutable myVariable = 0
myVariable <- 15

Inferencia de tipos y distinción de espacios en blanco

F# permite declarar variables y valores sin especificar su tipo, de modo que se podría suponer que F# es un lenguaje dinámico, pero no lo es. Es importante entender que F# es un lenguaje estático, al igual que C# o C++. Sin embargo, F# en un sistema eficaz de inferencia de tipos que permite evitar especificar los tipos de objetos en muchos lugares. Esto permite una sintaxis simple y sucinta y, al mismo tiempo, aún ofrece la seguridad de tipos de los lenguajes estáticos.

Aunque los sistemas de inferencia de tipos como éste en realidad no se encuentran en los lenguajes imperativos, la inferencia de tipos no está relacionada directamente con la programación funcional. Sin embargo, la inferencia de tipos es una noción fundamental que se debe comprender si se desea aprender F#. Afortunadamente, si usted es un desarrollador de C#, es muy probable que ya esté familiarizado con inferencia de tipos básica gracias a la palabra clave var:

// Here, the type is explictily given
Dictionary<string, string> dictionary = 
  new Dictionary<string, string>();

// but here, the type is inferred
var dictionary = new Dictionary<string, string>();

Ambas líneas de código C# crean nuevas variables que se escriben estáticamente como Dictionary<string, string>, pero la palabra clave var indica al compilador que infiera el tipo de variable por usted. F# lleva este concepto al siguiente nivel. Por ejemplo, la siguiente es una función add en F#:

let add x y =
    x + y
    
let four = add 2 2

No existe ni una sola anotación de tipo en el código anterior, pero F# Interactive revela los tipos estáticos:

val add : int -> int -> int
val four : int = 4

Explicaré las flechas en mayor detalle más adelante. Por ahora, puede interpretar que esto significa que add está definido para tomar dos argumentos int y que four es un valor int. El compilador F# pudo inferir esto basado en la forma en que add y four estaban definidos. Las reglas que usa el compilador para hacer esto van más allá del ámbito de este artículo, pero puede obtener más información acerca de ellas en el Centro de desarrolladores de F# si está interesado.

La inferencia de tipos es una manera en que F# reduce el ruido del código, pero tenga en cuenta que no hay llaves ni palabras clave que denoten el cuerpo o el valor de retorno de la función add. Esto se debe a que F# es un lenguaje que distingue espacios en blanco de manera predeterminada. En F#, el cuerpo de una función se indica mediante sangría y se devuelve un valor, asegurándose de que sea la última línea de la función. Al igual que la inferencia de tipos, la distinción de espacios en blanco no tiene relación directa con la programación funcional, pero usted debe estar familiarizado con el concepto para poder usar F#.

Efectos secundarios

Ahora usted sabe que la programación funcional es diferente a la programación imperativa, porque se basa en valores inmutables en lugar de variables mutables, pero este hecho no es muy útil por sí mismo. El siguiente paso es comprender los efectos secundarios.

En la programación imperativa, la salida de una función depende de su argumento de entrada y del estado actual del programa. En la programación funcional, las funciones sólo dependen de sus argumentos de entrada. En otras palabras, cuando se llama a una función más de una vez con el mismo valor de entrada, siempre se obtiene el mismo valor de salida. La razón por la que esto no sucede en la programación imperativa es debido a sus efectos secundarios, como se demuestra en la figura 1.

Figura 1 Efectos secundarios de las variables mutables

public MemoryStream GetStream() {
  var stream = new MemoryStream();
  var writer = new StreamWriter(stream);
  writer.WriteLine("line one");
  writer.WriteLine("line two");
  writer.WriteLine("line three");
  writer.Flush();
  stream.Position = 0;
  return stream;
}

[TestMethod]
public void CausingASideEffect() {
  using (var reader = new StreamReader(GetStream())) {
    var line1 = reader.ReadLine();
    var line2 = reader.ReadLine();

    Assert.AreNotEqual(line1, line2);
  }
}

En la primera llamada a ReadLine, la secuencia se lee hasta que se encuentra una nueva línea. Después, ReadLine devuelve todo el texto a la nueva línea. Entre esos pasos, se actualiza una variable mutable que representa la posición de la secuencia. Ese es el efecto secundario. En la segunda llamada a ReadLine, el valor de la variable de posición mutable ha cambiado, de modo que ReadLine devuelve un valor diferente.

Ahora observemos una de las consecuencias más significativas del uso de efectos secundarios. Primero, considere una clase PiggyBank simple y algunos métodos para trabajar con ella (consulte la figura 2).

Figura 2 PiggyBanks mutables

public class PiggyBank{
  public PiggyBank(int coins){
    Coins = coins;
  }

  public int Coins { get; set; }
}

private void DepositCoins(PiggyBank piggyBank){
  piggyBank.Coins += 10;
}

private void BuyCandy(PiggyBank piggyBank){
  if (piggyBank.Coins < 7)
    throw new ArgumentException(
      "Not enough money for candy!", "piggyBank");

  piggyBank.Coins -= 7;
}

Si tiene una piggy bank con 5 coins, puede llamar a DepositCoins antes de BuyCandy, pero si se invierte el orden se origina una excepción:

// this works fine
var piggyBank = new PiggyBank(5);
DepositCoins(piggyBank);
BuyCandy(piggyBank);
// but this raises an ArgumentException
var piggyBank = new PiggyBank(5);
BuyCandy(piggyBank);
DepositCoins(piggyBank);

La función BuyCandy y la función DepositCoins actualizan el estado de piggy bank a través del uso de un efecto secundario. Por consiguiente, el comportamiento de cada función depende del estado de piggy bank. Dado que el número de coins es mutable, el orden en el que se ejecutan estas funciones es significativo. En otras palabras, existe una dependencia de intervalos implícita entre estos dos métodos.

Ahora hagamos que el número de coins sea de sólo lectura para simular una estructura de datos inmutables. La figura 3 muestra que BuyCandy y DepositCoins ahora devuelven nuevos objetos PiggyBank en lugar de actualizar una PiggyBank existente.

Figura 3 PiggyBanks inmutables

public class PiggyBank{
  public PiggyBank(int coins){
    Coins = coins;
  }

  public int Coins { get; private set; }
}

private PiggyBank DepositCoins(PiggyBank piggyBank){
  return new PiggyBank(piggyBank.Coins + 10);
}

private PiggyBank BuyCandy(PiggyBank piggyBank){
  if (piggyBank.Coins < 7)
    throw new ArgumentException(
      "Not enough money for candy!", "piggyBank");

  return new PiggyBank(piggyBank.Coins - 7);
}

Al igual que antes, si intenta llamar a BuyCandy antes de DepositCoins, recibirá una excepción de argumento:

// still raises an ArgumentException
var piggyBank = new PiggyBank(5);
BuyCandy(piggyBank);
DepositCoins(piggyBank);

Pero ahora, aunque revierta el orden, obtendrá el mismo resultado:

// now this raises an ArgumentException,  too!
var piggyBank = new PiggyBank(5);
DepositCoins(piggyBank);
BuyCandy(piggyBank);

Aquí, BuyCandy y DepositCoins sólo dependen de su argumento de entrada dado que el número de coins es inmutable. Puede ejecutar las funciones en cualquier orden y el resultado será el mismo. La dependencia de tiempo implícita desaparece. Sin embargo, dado que probablemente desee que BuyCandy tenga éxito, debe hacer que el resultado de BuyCandy dependa de la salida de DepositCoins. Debe hacer que la dependencia sea explícita:

var piggyBank = new PiggyBank(5);
BuyCandy(DepositCoins(piggyBank));

Esta es una diferencia sutil con consecuencias de gran alcance. El estado mutable compartido y las dependencias implícitas son el origen de algunos de los errores más complicados del código imperativo y son la razón por la cual el subprocesamiento múltiple resulta tan difícil en los lenguajes imperativos. Cuando debe preocuparse por el orden en el que se ejecutan las funciones, está obligado a confiar en incómodos mecanismos de bloqueo para mantener las cosas en orden. Los programas puramente funcionales están libres de efectos secundarios y de dependencias de tiempo implícitas, por lo que no importa el orden en que se ejecuten las funciones. Esto significa que no necesita preocuparse por mecanismos de bloqueo ni por otras técnicas de subprocesamiento múltiple propensas a errores.

El subprocesamiento múltiple más sencillo es una razón importante por la cual los programas funcionales reciben atención últimamente, pero existen muchos otros beneficios de la programación en estilo funcional. Las funciones sin efectos secundarios son más fáciles de probar debido a que cada función se basa solamente en sus argumentos de entrada. Son más fáciles de mantener, debido a que no se basan implícitamente en lógica de otras funciones de configuración. Las funciones sin efectos secundarios también tienden a ser más pequeñas y fáciles de combinar. En breve, abordaré este último punto con mayor detalle.

En F#, el enfoque está en la evaluación de valores de resultados de funciones y no en sus efectos secundarios. En lenguajes imperativos, es común llamar a una función para que haga algo; en los lenguajes funcionales, se llama a las funciones para que devuelvan un resultado. Puede ver esto en F# al consultar la instrucción if:

let isEven x =
    if x % 2 = 0 then
        "yes"
    else
        "no"

Usted sabe que en F#, la última línea de una función es el valor de retorno de ésta, pero en este ejemplo, la última línea de la función es la instrucción if. Esto no es un truco del compilador. En F#, incluso las instrucciones if están diseñadas para devolver valores:

let isEven2 x =
    let result = 
        if x % 2 = 0 then
            "yes"
        else
            "no"
    result

El valor result es del tipo string y está asignado directamente a la instrucción if. Es similar a la forma en que el operador condicional funciona en C#:

string result = x % 2 == 0 ? "yes" : "no";

El operador condicional pone énfasis en el retorno de un valor por sobre la causa de un efecto secundario. Es un enfoque más funcional. En contraste, la instrucción if de C# es más imperativa, dado que no devuelve ningún resultado. Todo lo que puede hacer es ocasionar efectos secundarios.

Composición de funciones

Ahora que ha observado algunos de los beneficios de las funciones sin efectos secundarios, está listo para usar funciones en su pleno potencial en F#. Primero, comencemos con algún código C# para elevar al cuadrado los números cero a 10:

IList<int> values = 0.Through(10).ToList();
IList<int> squaredValues = new List<int>();
for (int i = 0; i < values.Count; i++) {
  squaredValues.Add(Square(values[i]));
}

Además de los métodos auxiliares Through y Square, este código es C# bastante estándar. Los buenos desarrolladores de C# probablemente se ofendan por mi uso de un bucle for en lugar de un bucle foreach, y con razón. Los lenguajes modernos como C# ofrecen bucles foreach como abstracción, para facilitar el paso a través de las enumeraciones al eliminar la necesidad de indizadores explícitos. Tienen éxito en cuanto a este objetivo, pero tome en consideración el código de la figura 4.

Figura 4 Uso de bucles foreach

IList<int> values = 0.Through(10).ToList();

// square a list
IList<int> squaredValues = new List<int>();

foreach (int value in values) {
  squaredValues.Add(Square(value));
}

// filter out the even values in a list
IList<int> evens = new List<int>();

foreach(int value in values) {
  if (IsEven(value)) {
    evens.Add(value);
  }
}

// take the square of the even values
IList<int> results = new List<int>();

foreach (int value in values) {
  if (IsEven(value)) {
    results.Add(Square(value));
  }
}

Los bucles foreach de este ejemplo son similares, pero cada cuerpo de bucle realiza una operación levemente diferente. Los programadores imperativos tradicionalmente no han tenido problema con esta duplicación de código, dado que se considera código idiomático.

Los programadores funcionales lo consideran desde otro punto de vista. En lugar de crear abstracciones como bucles foreach para ayudarse a pasar por las listas, usan funciones sin efectos secundarios:

let numbers = {0..10}
let squaredValues = Seq.map Square numbers

Este código F# también eleva al cuadrado una secuencia de números, pero lo hace mediante una función de orden superior. Las funciones de orden superior son simplemente funciones que aceptan a otra función como argumento de entrada. En este caso, la función Seq.map acepta a la función Square como argumento. Aplica esta función a cada número de la secuencia numbers y devuelve la secuencia de números elevados al cuadrado. Las funciones de orden superior son la razón por la que muchas personas afirman que la programación funcional usa funciones como datos. Esto sólo significa que las funciones se pueden usar como parámetros o se pueden asignar a un valor o variable una al igual que una int o string. En términos de C#, es muy similar a los conceptos de delegados y expresiones lambda.

Las funciones de orden superior son una de las técnicas que hacen que la programación funcional sea tan eficaz. Puede usar funciones de orden superior para aislar el código duplicado dentro de bucles foreach y encapsularlo en funciones independientes y sin efectos secundarios. Cada una de estas funciones realiza una pequeña operación que el código dentro del bucle foreach habría administrado. Gracias a que no tienen efectos secundarios, es posible combinar estas funciones para crear código más legible y fácil de mantener, que logre lo mismo que los bucles foreach:

let squareOfEvens = 
    numbers
    |> Seq.filter IsEven
    |> Seq.map Square

Lo único confuso acerca de este código puede ser el operador |>. Este operador se usa para que el código sea más legible, al permitir reordenar los argumentos en una función, de manera que el último argumento sea el primero que usted lea. Su definición es muy simple:

let (|>) x f = f x

Sin el operador |>, el código squareOfEvens se vería así:

let squareOfEvens2 = 
  Seq.map Square (Seq.filter IsEven numbers)

Si usa LINQ, el empleo de funciones de orden superior de esta forma debe parecer muy conocido. Esto se debe a que LINQ está profundamente enraizado en la programación funcional. De hecho, es posible traducir el problema square of evens muy fácilmente a C# mediante métodos de LINQ:

var squareOfEvens =
  numbers
  .Where(IsEven)
  .Select(Square);

Esto se traduce en la siguiente sintaxis de consulta de LINQ:

var squareOfEvens = from number in numbers
  where IsEven(number)
  select Square(number);

El uso de LINQ en código C# o Visual Basic permite aprovechar parte de la eficacia de la programación funcional diariamente. Es una excelente manera de aprender técnicas de programación funcional.

Al comenzar a usar funciones de orden superior regularmente, tarde o temprano llegará a un punto en que desee crear una función pequeña y muy específica para pasar a una función de orden superior. Los programadores funcionales usan funciones lambda para resolver este problema. Las funciones lambda son simplemente funciones que se definen sin asignarles un nombre. Normalmente son pequeñas y tienen un uso muy específico. Por ejemplo, la siguiente es otra forma en la que se pueden elevar al cuadrado números pares mediante una lambda:

let withLambdas =
    numbers
    |> Seq.filter (fun x -> x % 2 = 0)
    |> Seq.map (fun x -> x * x)

La única diferencia entre esto y el código anterior para elevar al cuadrado números pares y es que Square e IsEven se definen como lambdas. En F#, una función lambda se declara usando la palabra clave fun. Sólo debe usar lambdas para declarar funciones con un solo uso, debido a que no se pueden usar fácilmente fuera del ámbito en el que están definidas. Por esta razón, Square e IsEven son opciones deficientes de funciones lambda, ya que sirven para muchas otras situaciones.

Currificación y aplicación parcial

Ahora conoce casi todos los aspectos básicos que necesita para comenzar a trabajar con F#, pero hay un concepto más con el que debe estar familiarizado. En ejemplos anteriores, el operador |> y las flechas en las firmas de tipo de F# Interactive están relacionadas con un concepto conocido como currificación.

La currificación significa descomponer una función con muchos argumentos en una serie de funciones, cada una de las cuales toma un argumento y, finalmente, produce el mismo resultado que la función original. La currificación es probablemente uno de los temas más complicados de este artículo para un desarrollador de .NET, especialmente porque con frecuencia se confunde con la aplicación parcial. Puede observar cómo funcionan ambas en este ejemplo:

let multiply x y =
    x * y
    
let double = multiply 2
let ten = double 5

Desde ya, debe observar un comportamiento que es diferente de la mayoría de los lenguajes imperativos. La segunda instrucción crea una nueva función llamada double pasando un argumento a una función que toma dos. El resultado es una función que acepta un argumento int y devuelve la misma salida que si hubiese llamado a multiply con x igual a 2 e y igual a dicho argumento. En términos de comportamiento, es lo mismo que este código:

let double2 z = multiply 2 z

Con frecuencia, las personas dicen erróneamente que multiply recibe currificación para formar double. Pero esto es verdadero sólo en parte. La función multiply recibe currificación, pero eso ocurre cuando está definida, ya que las funciones en F# reciben currificación de manera predeterminada. Cuando se crea la función double, es más preciso decir que la función multiply se aplica parcialmente.

Observemos estos pasos con mayor detalle. La currificación descompone una función con muchos argumentos en una serie de funciones, cada una de las cuales toma un argumento y, finalmente, producen el mismo resultado que la función original. La función multiply tiene la siguiente firma de tipo según F# Interactive:

val multiply : int -> int -> int

Hasta ahora, usted ha descifrado que esto significa que multiply es una función que toma dos argumentos int y devuelve un resultado int. Ahora le explicaré lo que ocurre en realidad. La función multiply es realmente una serie de dos funciones. La primera función toma un argumento int y devuelve otra función, lo que de hecho enlaza x con un valor específico. Esta función también acepta un argumento int que se puede considerar como el valor con el cual enlazar a y. Después de llamar a esta segunda función, x e y se enlazan, de modo que el resultado es el producto de x e y según se define en el cuerpo de double.

Para crear double, la primera función de la cadena de funciones de multiply se evalúa para aplicar parcialmente multiply. A la función resultante se le asigna el nombre double. Cuando double se evalúa, usa su argumento junto con el valor aplicado parcialmente para crear el resultado.

Uso de F# y de la programación funcional

Ahora que conoce suficiente vocabulario para comenzar a usar F# y programación funcional, puede elegir entre varias opciones a continuación.

F# Interactive permite explorar código F# y acumular rápidamente scripts de F#. También es útil para validar preguntas cotidianas acerca del comportamiento de funciones de la biblioteca .NET sin recurrir a archivos de ayuda o a búsquedas en la Web.

F# se distingue en la expresión de algoritmos complicados, de modo que puede encapsular estas porciones de sus aplicaciones en bibliotecas F# que se pueden llamar desde otros lenguajes de .NET. Esto es especialmente útil en aplicaciones de ingeniería o situaciones de subprocesamiento múltiple.

Por último, se pueden usar técnicas de programación funcional para desarrollo cotidiano de .NET sin siquiera escribir código F#. Use LINQ en lugar de bucles for o foreach. Pruebe el uso de delegados para crear funciones de orden superior. Limite el uso de la mutabilidad y los efectos secundarios en su programación imperativa. Una vez que comience a escribir código en estilo funcional, pronto deseará poder escribir más código F#.                     

Chris Marinos es un consultor de software de SRT Solutions en Ann Arbor, Mich. Puede escucharlo hablar sobre F#, programación funcional y otros fascinantes temas en eventos cerca del área de Ann Arbor o en su blog disponible en srtsolutions.com/blogs/chrismarinos.

Gracias a los siguientes expertos técnicos por su ayuda en la revisión de este artículo: Luke Hoban