Apuntes

Lenguaje de programación

  • Familia: ML (SML u OCaml)
  • Notación matemática: la sintaxis de su código fuente se basa en la declaración de tipos y valores. En apariencia se definen ecuaciones, igualdades o relaciones:

     > let (x:byte) = 123uy;;
     val x : byte = 123uy
     > let (x:string) = "Hola mundo!";;
     val x : string = "Hola mundo!"
     > let x = 2 * 5;;
     val x : int = 10
     > let y = x = 10;;
     val y : bool = true
     > let z = if 4 = 2 + 3 then 10 else 20;;
     val z : int = 20
     > let z = if (4 = (2 + 3)) then 10 else 20;;
     val z : int = 20
    
  • Compilado e Interpretado: el código fuente puede ser compilado (extensión fs) o bien interpretado (extensión fsx). A partir del código fuente, el compilador (fsc.exe o fsharpc) generará un archivo binario ejecutable mientras que el interprete (fsi.exe o fsharpi) permitirá interactuar con él.

  • Tipado Estático: los tipos de datos son identificados (inferidos) en tiempo de compilación manteniendose durante la ejecución del programa. Debido a la existencia de la inferencia de tipo no se requiere especificar a estos.

     > "Hola" + " " + "mundo";;
     val it : string = "Hola mundo"
     > 5 + 10 + 15;;
     val it : int = 30
     > 5.4 + 10.3 + 15.3;;
     val it : float = 31.0
    
  • Evaluación de expresiones: en el código fuente se establecen expresiones las cuales son evaluadas o resueltas estrictamente. Las expresiones se escriben mediante ecuaciones.

  • Funcional: las funciones son ciudadanos de primera clase por lo que pueden ser usados como parámetros o valores regresados por otras funciones.

     > fun () -> "Hola" + " " + "mundo";;
     val it : unit -> string = <fun:clo@22-6>
     > (fun () -> "Hola" + " " + "mundo")();;
     val it : string = "Hola mundo"
     > (fun x -> 2 * x)(6);;
     val it : int = 12
     > (fun f -> f 6)(fun x -> 2 * x);;
     val it : int = 12
     > ((fun x -> fun y -> x - y)(5))(8);; 
     val it : int = -3
    

Expresiones

Literales:

  • Unidad: () o unit, representa a un valor o resultado sin sentido o significado. Similar al concepto de void.

     > ();;
     val it : unit = ()
    
  • Números: -567, 45, 6.91, -5.348

    • byte: desde 0 hasta 255. Sufijo: uy
    • sbyte: desde -128 hasta 127. Sufijo: y
    • int16: desde -32768 hasta 32767. Sufijo: s
    • uint16: desde 0 hasta 65535. Sufijo: us
    • int: desde -2,147,483,648 hasta 2,147,483,647.
    • uint32: desde 0 hasta 4,294,967,295. Sufijo: u
    • int64: desde -9,223,372,036,854,775,808 hasta 9,223,372,036,854,775,807. Sufijo: L
    • uint64: desde 0 hasta 18,446,744,073,709,551,615. Sufijo: UL
    • float32: de 32-bits (desde -3.402823e38 hasta 3.402823e38) Sufijo: f
    • float: de 64-bits (desde -1.79769313486232e308 hasta 1.79769313486232e308)
  • Booleanos:
    • bool: true y false.
  • Caracteres:
    • char: 'a', '\\', '@', ' ', '\t'.
  • Cadenas de caracteres:
    • string: "Hola mundo!", "a", "\\", "Hola\t\t\tmundo\t\t\t!".

Variables:

  • Nombres dado a valores: x, y, altura, base.

Colecciones:

  • Tuplas: agrupación de valores literales de distinto tipo en orden.

     > (10, "Hola", true, '@');;
     val it : int * string * bool * char = (10, "Hola", true, '@')
    
  • Listas: agrupación de valores literales de un mismo tipo en orden.

    • Lista de cadenas de caracteres (string list):

         > ["Hola"; " "; "mundo"; "!"];;
         val it : string list = ["Hola"; " "; "mundo"; "!"]
      
    • Lista de números enterors (int list):

         > [10; 20; 30; 40; 50];;
         val it : int list = [10; 20; 30; 40; 50]
      
    • Lista de booleanos (bool list):

         > [true; false; false; true];;
         val it : bool list = [true; false; false; true]
      
  • Arreglos: agrupación de valores literales de un mismo tipo, indexados y en orden.

    • Arreglo de cadenas de caracteres (string[]):

         > [|"Hola"; " "; "mundo"; "!"|];;
         val it : string [] = [|"Hola"; " "; "mundo"; "!"|]
      
    • Arreglo de números enterors (int[]):

         > [|10; 20; 30; 40; 50|];;
         val it : int [] = [|10; 20; 30; 40; 50|]
      

      Acceso a uno de los elementos en base a su indice:

         > [|10; 20; 30; 40; 50|].[2];;
         val it : int = 30
         > [|10; 20; 30; 40; 50|].[2] + [|10; 20; 30; 40; 50|].[4];;
         val it : int = 80
      
    • Arreglo de booleanos (bool[]):

         > [|true; false; false; true|];;
         val it : bool [] = [|true; false; false; true|]
      
  • Registros: agrupación de pares llave-valor de distinto tipo sin orden.

    Definición:

     > type Cuadrado = { lado : float };;
     type Cuadrado =
       {lado: float;}
    

    Uso:

     > {lado = 3.5};;
     val it : Cuadrado = {lado = 3.5;}
     > let unCuadradoPequeño = {lado = 2.5};;
     val unCuadradoPequeño : Cuadrado = {lado = 2.5;}
     > let unCuadradoMásGrande = {lado = 7.2};;
     val unCuadradoMásGrande : Cuadrado = {lado = 7.2;}
    

    Definición:

     > type Triangulo =
          {
               baseDelTriangulo :  float;
               alturaDelTriangulo : float;
          };;
     type Triangulo =
       {baseDelTriangulo: float;
        alturaDelTriangulo: float;}
    

    Uso:

     > {baseDelTriangulo = 50.0; alturaDelTriangulo = 3.0};;
     val it : Triangulo = {baseDelTriangulo = 50.0;
                           alturaDelTriangulo = 3.0;}
     > let unTriangulito = {baseDelTriangulo = 0.5; alturaDelTriangulo = 0.3};;
     val unTriangulito : Triangulo = {baseDelTriangulo = 0.5;
                                      alturaDelTriangulo = 0.3;}
     > let unTriangulon = {baseDelTriangulo = 5000.0; alturaDelTriangulo = 30000.0};;
     val unTriangulon : Triangulo = {baseDelTriangulo = 5000.0;
                                     alturaDelTriangulo = 30000.0;}
    

    Definición:

     > type Alumno =
          {
              clave : string;
              nombre : string;
              apellidos : string;
              edad : int;
              sexo : char;
          };;
     type Alumno =
       {clave: string;
        nombre: string;
        apellidos: string;
        edad: int;
        sexo: char;}
    

    Uso:

     > {
            clave = "a123";
            nombre = "Juan";
            apellidos = "Perez Perez";
            edad = 13;
            sexo = 'm';
       };;
     val it : Alumno = {clave = "a123";
                 nombre = "Juan";
                 apellidos = "Perez Perez";
                 edad = 13;
                 sexo = 'm';}
     > let unaBodoquita = {
            clave = "a123";
            nombre = "Juana";
            apellidos = "Perez Perez";
            edad = 14;
            sexo = 'f';
       };;
     val unaBodoquita : Alumno = {clave = "a123";
                           nombre = "Juana";
                           apellidos = "Perez Perez";
                           edad = 14;
                           sexo = 'f';}
    

Comentarios:

  • Los caracteres // inician el comentario hasta el fin de la línea en que se encuentran:

     // Este es un comentario, de una sola línea
    
  • Para comentarios de varías líneas, iniciar con (* y terminar con *).

     (*
     Este es un comentario
     de varías líneas
     *)
    

Lectura de evaluación de expresiones

Siendo F# un lenguaje de programación con un sistema de tipos fuerte y la existencia en él de la inferencia de tipos, se vuelve importante el saber leer las respuestas dadas por el interprete con respecto a los tipos de datos.

images/01.png

  • El valor de eso tiene tipo byte con signo (signed byte) y es el -12:

     > -12y;;
     val it : sbyte = -12y
    
  • El valor de eso tiene tipo byte sin signo (unsigned byte) y es el 130:

     > 130uy;;
     val it : byte = 130uy
    
  • El valor de eso tiene tipo cadena de caracteres (string) y es "Hola mundo":

     > "Hola mundo!";;
     val it : string = "Hola mundo!"
    
  • El valor de eso tiene tipo booleano (bool) y es true:

     > true && true;;
     val it : bool = true
    
  • El valor de eso tiene tipo booleano (bool) y es false:

     > not true;;
     val it : bool = false
    
  • El valor de eso tiene tipo carácter (char) y es el carácter 1:

     > '1';;
     val it : char = '1'
    

Tuplas

images/02.png

  • El valor de eso tiene tipo bool seguido de int seguido de string seguido de float y es true seguido de 9 seguido de ! seguido de 4.3:

     > (true, 9, "!", 4.3);;
     val it : bool * int * string * float = (true, 9, "!", 4.3)
    

Funciones

Función anónima

Su definición se establece mediante la palabra reservada fun y al carecer de nombre su uso práctico implica su aplicación inmediata, ser usada como parámetro/devolución de otras funciones o bien ser enlazada (binding) en un ámbito.

Importante: el tema de tipo (tipado o sistema de tipos en Teoría de Lenguajes de Programación) cobra una mayor importancia ya que se entendería a una función como la relación/asociación entre un elemento de un conjunto-tipo, con algún otro elemento de otro conjunto-tipo.

> fun (x:byte) -> (x + x):byte;;
val it : x:byte -> byte = <fun:clo@9-20>

images/03.png

Al ser aplicada la función anónima con 10uy (unsigned byte) como valor de tipo byte, se obtiene a 20uy como valor de tipo byte:

> (fun (x:byte) -> (x + x):byte)(10uy);;
val it : byte = 20uy

Siguiendo la misma noción de tipo, aplicando la misma función anónima con 10 (signed int) se obtiene un error de tipo:

> (fun (x:byte) -> (x + x):byte)(10);;
  (fun (x:byte) -> (x + x):byte)(10);;
  -------------------------------^^
error FS0001: This expression was expected to have type
  byte    
but here has type
  int 

Considerando lo anterior, se puede definir y aplicar la siguiente función anónima:

> (fun (x:int) -> (x + x):int)(10);;
val it : int = 20
> (fun (x:int) -> (x + x):int)(-10);;
val it : int = -20

Lectura:

Es importante saber leer la firma o definición de una función:

  • Función que va de byte a byte:

    images/04.png

    Aplicaciones:

     > (fun (x:byte) -> (x + x):byte)(127uy);;
     val it : byte = 254uy
     > (fun (x:byte) -> (x + x):byte)(128uy);;
     val it : byte = 0uy
     > (fun (x:byte) -> (x + x):byte)(129uy);;
     val it : byte = 2uy
    
  • Función que va de int a int:

     > fun (x:int) -> (x + x):int;;
     val it : x:int -> int = <fun:clo@52-4>
     > abs;;
     val it : (int -> int) = <fun:it@3>
    

    Aplicaciones:

     > (fun (x:int) -> (x + x):int)(128);;
     val it : int = 256
     > (fun (x:int) -> (x + x):int)(129);;
     val it : int = 258
     > abs -234;;
     val it : int = 234
    
  • Función que va de char a int:

     > (fun (x:char) -> int x);;
     val it : x:char -> int = <fun:clo@18-1>
     > (fun (x:char) -> int x)('A');;
     val it : int = 65
     > (fun (x:char) -> int x)('B');;
     val it : int = 66
     > (fun (x:char) -> int x)('a');;
     val it : int = 97
     > (fun (x:char) -> int x)('b');;
     val it : int = 98
    
  • Función que va de string a string:

     > fun (x:string) -> (x + x):string;;
     val it : x:string -> string = <fun:clo@3-18>
    

    Aplicaciones:

     > (fun (x:string) -> (x + x):string)("Hola");;
     val it : string = "HolaHola"
     > (fun (x:string) -> (x + x):string)("mundo!");;
     val it : string = "mundo!mundo!"
    
  • Función que va de int a float:

     > float;;
     val it : (int -> float) = <fun:it@53-2>
    

    Aplicaciones:

     > float 10;;
     val it : float = 10.0
     > float -37;;
     val it : float = -37.0
    
  • Función que va de un objeto generico/literal simple a string:

     > string;;
     val it : (obj -> string) = <fun:it@58-4>
    

    Aplicaciones:

     > string 10;;
     val it : string = "10"
     > string true;;
     val it : string = "True"
     > string -0.44;;
     val it : string = "-0.44"
    
  • Función que va de string a int:

     > fun (x:string) -> (x.Length):int;;
     val it : x:string -> int = <fun:clo@4-5>
    

    Aplicaciones:

     > (fun (x:string) -> (x.Length):int)("Hola mundo!");;
     val it : int = 11
     > (fun (x:string) -> (x.Length):int)("Adios mundo!");;
     val it : int = 12
     > (fun (x:string) -> (x.Length):int)("Hola" + "mundo!");;
     val it : int = 10
    
  • Función que va de bool a string:

     > fun (x:bool) -> (if x = true then "Hola" else "Adios"):string;;
     val it : x:bool -> string = <fun:clo@8-6>
    

    Aplicaciones:

     > (fun (x:bool) -> (if x = true then "Hola" else "Adios"):string)(true);;
     val it : string = "Hola"
     > (fun (x:bool) -> (if x = true then "Hola" else "Adios"):string)(false);;
     val it : string = "Adios"
    

    Error de tipo:

     > (fun (x:bool) -> (if x = true then "Hola" else "Adios"):string)(-345);;
       (fun (x:bool) -> (if x = true then "Hola" else "Adios"):string)(-345);;
       ----------------------------------------------------------------^^^^
     error FS0001: This expression was expected to have type
       bool    
     but here has type
       int
    
  • Función que va de int a bool:

     > (fun (x:int) -> (x > 0):bool);;
     val it : x:int -> bool = <fun:clo@26-2>
    

    Aplicaciones:

     > (fun (x:int) -> (x > 0):bool)(-38);;
     val it : bool = false
     > (fun (x:int) -> (x > 0):bool)(943);;
     val it : bool = true
    
  • Función que va de string a bool:

     > (fun (x:string) -> (if x.Length <> 0 then false else true):bool);;
     val it : x:string -> bool = <fun:clo@43-5>
     > (fun (x:string) -> (if x.Length <> 0 then false else true):bool)("Hola");;
     val it : bool = false
     > (fun (x:string) -> (if x.Length <> 0 then false else true):bool)("");;
     val it : bool = true
    

Función de orden superior

Es una función (anónima o no-anónima) la cual puede recibir como parámetro alguna función, regresar una función como resultado o bien ambos casos.

> fun (baseDelTriangulo:float) -> (fun (alturaDelTriangulo:float) -> (baseDelTriangulo * alturaDelTriangulo) / 2.0);;
val it : baseDelTriangulo:float -> alturaDelTriangulo:float -> float = <fun:clo@3-13>

Lo anterior se entiende como una función que va de float a una función que va de float a float, o de otra forma:

> fun (baseDelTriangulo:float) ->
       (fun (alturaDelTriangulo:float) ->
            (baseDelTriangulo * alturaDelTriangulo) / 2.0);;
val it : baseDelTriangulo:float -> alturaDelTriangulo:float -> float = <fun:clo@3-14>

Se entiende que una función recibe la baseDelTriangulo, esta regresa una función que espera la alturaDelTriangulo y esta última función es quien computaría la expresión (baseDelTriangulo * alturaDelTriangulo) / 2.0):

  • Aplicación de la función que recibe la baseDelTriangulo:

     > (fun (baseDelTriangulo:float) ->
            (fun (alturaDelTriangulo:float) ->
                 (baseDelTriangulo * alturaDelTriangulo) / 2.0))(50.0);;
     val it : (float -> float) = <fun:it@19-5>
    
  • Aplicación de la función que recibe la alturaDelTriangulo:

     > ((fun (baseDelTriangulo:float) ->
             (fun (alturaDelTriangulo:float) ->
                  (baseDelTriangulo * alturaDelTriangulo) / 2.0))(50.0))(3.0);;
     val it : float = 75.0
    

Bindings

Un binding (enlace o vínculo) es el establecimiento de un nombre con un valor dentro de cierto ámbito.

let

Establece el enlace entre un identificador y un valor dentro de cierto ámbito: el identificador será el nombre de la variable y el valor será el resultado de la evaluación de una expresión.

> let elAntecesorDe = fun (x:int) -> (x - 1):int;;
val elAntecesorDe : x:int -> int
> let elSucesorDe = fun (x:int) -> (x + 1):int;;
val elSucesorDe : x:int -> int
> let unSaludoPara = fun (nombre:string) -> ("Hola " + nombre):string;;
val unSaludoPara : nombre:string -> string
> elAntecesorDe 10;;
val it : int = 9
> elSucesorDe 20;;
val it : int = 21
> unSaludoPara "mundo!";;
val it : string = "Hola mundo!"

Es posible usar a let para enlazar de forma local a variables:

  • Definición: Sea elAntecesorYElSucesorDe una función que va de int a int seguido de int:

     > let elAntecesorYElSucesorDe = fun (x:int) ->
           let elAntecesorDe = fun (x:int) -> (x - 1):int
           let elSucesorDe = fun (x:int) -> (x + 1):int
           (elSucesorDe x, elAntecesorDe x);;
     val elAntecesorYElSucesorDe : x:int -> int * int
    

    Aplicación: El valor de eso tiene tipo int seguido de int y es el 16 seguido de el 14:

     > elAntecesorYElSucesorDe 15;;
     val it : int * int = (16, 14)
    

Lectura de definición y aplicación de funciones

La definición y aplicación de funciones en F# puede parecer confusa en ocasiones debido a:

  • Presencia de un sistema de tipo fuerte.
  • Existe la inferencia de tipos.
  • Sintaxis basada en la notación matemática, por lo que:
    • Suele evitarse el abuso de paréntesis.
    • Existe la misma idea de precedencia de operadores, encontrada en otros lenguajes de programación.

Si en LISP uno abusa de los paréntesis, en ML uno suele evitarlos ;-)

Un documento que establece una critica abierta a LISP es Why calculating is better than scheming por Philip Wadler, uno de los diseñadores del lenguaje de programación Haskell y contribuyente al lenguaje de programación Java. Lectura: Por que calcular es mejor que esquematizar?.

  • Función anónima con anotación del tipo de parámetro y anotación del tipo de valor devuelto:

     > fun (x:float) -> (x * x):float;;
     val it : x:float -> float = <fun:clo@46-11>
     > (fun (x:float) -> (x * x):float)(2.5);;
     val it : float = 6.25
    

    Misma función: no se requiere anotar el tipo de valor devuelto por la inferencia de tipo a partir del tipo de parámetro y el uso de la función ____*:

     > (fun (x:float) -> x * x)(2.5);;
     val it : float = 6.25
    

    Misma función: no se requieren de los paréntesis en el parámetro ya que la definición de la función espera un único parámetro. La asociación de operandos y operadores es de izquierda a derecha:

     > (fun (x:float) -> x * x) 2.5;;
     val it : float = 6.25
    

    Esta última forma sería "la más común".

  • Función anónima que recibe un solo parámetro que contiene un float seguido de un float, regresando un float:

     > fun (x:float, y:float) -> (x * y):float;;
     val it : x:float * y:float -> float = <fun:clo@48-16>
     > (fun (x:float, y:float) -> (x * y):float)(2.5, 2.0);;
     val it : float = 5.0
    

    Misma función:

     > (fun (x:float, y:float) -> x * y)(2.5, 2.0)
     val it : float = 5.0
    
  • Función anónima que recibe dos parámetros, primero un float y despúes otro float:

     > fun (x:float) (y:float) -> (x * y):float;;
     val it : x:float -> y:float -> float = <fun:clo@50-18>
    

    A lo anterior se le puede conocer como Función Currificada o Función en forma Curry (en honor de Haskell Curry). Su lectura sería: función que va de float (x) a una función que va de float (y) a float (x * y).

    La currificación (palabra inventada) de una función es una técnica mediante la cual una función de n parámetros es transformada en una función que recibe el primer parámetro regresa una función que espera el próximo parámetro y esta a su vez regresa una función que espera el siguiente parámetro hasta obtener los n parámetros.

    Otra forma de observar lo anterior es: todas las funciones son definidas con un solo parámetro.

    Misma función:

     > (fun (x:float) (y:float) -> (x * y)) 2.5;;
     val it : (float -> float) = <fun:it@50-4>
     > (fun (x:float) (y:float) -> (x * y)) 2.5 2.0;;
     val it : float = 5.0
    
  • Función currificada sin anotación de los tipos de parámetros y sin anotación del tipo del valor devuelto tomando ventaja de la inferencia de tipos:

     > (fun x y z -> 1.0 * x * y * z) 2.5 2.0 3.0;;
     val it : float = 15.0
    

    La currificación de funciones implica la existencia de closures: la tercera y última función, la que espera a z, aún tiene acceso al parámetro x de la primera función.

     > (fun x y z -> 1.0 * x) 2.5 2.0 3.0;;
     val it : float = 2.5
    

    F# previene el ocultamiento (shadowing) de los parámetros:

     > (fun x x x -> 1.0 * x) 2.5 2.0 3.0;;
     error FS0038: 'x' is bound twice in this pattern
    

    Aunque es posible aún contemplar el ocultamiento de la siguiente forma, poco común:

     > (fun x -> fun x -> fun x -> 2.0 * x) 2.5 2.0 3.0;;
     val it : float = 6.0
    
  • Función con nombre: la función se llama areaDeUnTriangulo y va de un float a una función que va de un float a float. Ambas expresiones definen la misma función: observa lo impreso por el interprete:

     > let areaDeUnTriangulo = fun baseDelTriangulo ->
                                   fun alturaDelTriangulo ->
                                       (baseDelTriangulo * alturaDelTriangulo) / 2.0
     val areaDeUnTriangulo : baseDelTriangulo:float -> alturaDelTriangulo:float -> float
    

    O bien:

     > let areaDeUnTriangulo baseDelTriangulo alturaDelTriangulo =
           (baseDelTriangulo * alturaDelTriangulo) / 2.0
     val areaDeUnTriangulo : baseDelTriangulo:float -> alturaDelTriangulo:float -> float
    

    Aplicación de la función:

     > areaDeUnTriangulo 5.0 30.0;;
     val it : float = 75.0
    

Aspectos de la inferencia de tipo

Sin lugar a duda la inferencia de tipo ayuda a una escritura mucho más ligera del código en F#. Sin embargo es necesario prestar atención cuando se establecen algunas expresiones para las cuales no se puede resolver con exactitud su tipo, debido a la falta de información.

> let f x = if x then "hola" else "adios";;
val f : x:bool -> string

En este caso, la función f va de bool a string. Sin embargo:

> let f x = x;;
val f : x:'a -> 'a

La anterior función es la función de identidad y no se ha podido inferir el tipo de x en f al faltar la información de lo que se ha de hacer con x. El interprete solo logra indicar una variable de tipo: 'a. Una variable de tipo indica que puede ser cualquier tipo (generic type) el que ocupe su lugar:

> f -234;;
val it : int = -234
> f true;;
val it : bool = true
> f "Hola mundo!";;
val it : string = "Hola mundo!"
> f (false, 4.2);;
val it : bool * float = (false, 4.2)
> f abs;;
val it : (int -> int) = <fun:it@7-5>

Por cuestiones "culturales" en algunos lenguajes de programación a las variables de tipo se les suele pronunciar como las letras del alfabeto griego clásico, de tal forma que: 'a es alfa, 'b es beta, 'c es gama, 'd es delta, etc.

Ejemplos:

  • Función (f) que va de una función (g) que va de bool a alfa a alfa:

     > let f g = g true;;
     val f : g:(bool -> 'a) -> 'a
    

    Aplicaciones:

     > f (fun x -> not x);;
     val it : bool = false
     > f (fun x -> if x then ":)" else ":(");;
     val it : string = ":)"
     > f (fun x -> (x, x));;
     val it : bool * bool = (true, true)
    
  • Función (f) que va de x que tiene tipo alfa a una función que va de g que va de alfa a beta a beta:

     > let f x = fun g -> g x;;
     val f : x:'a -> g:('a -> 'b) -> 'b
    

    Aplicaciones:

     > (f -5)(fun x -> 2 * x);;
     val it : int = -10
     > (f -5)(fun x -> 3 * x);;
     val it : int = -15
     > (f -5)(fun x -> if x < 0 then true else false);;
     val it : bool = true
     > (f true)(fun x -> if x then ":)" else ":(");;
     val it : string = ":)"
    

    Sin embargo se ha de considerar la correcta aplicación de este tipo de funciones:

     > f -5;;
     f -5;;
     ^^^^
     error FS0030: Value restriction. The value 'it' has been inferred to have generic type
         val it : ((int -> '_a) -> '_a)    
     Either make the arguments to 'it' explicit or, if you do not intend for it to be generic, add a type annotation.
    

Forward Pipe

Forwar Pipe (|>) es un operador que dado una expresión a su izquierda y una función a su derecha, reenvía el valor de la expresión a su izquierda como parámetro de la función a su derecha.

  • Partiendo de las siguientes funciones:

     > let elTripleDe x = 3 * x;;
     val elTripleDe : x:int -> int
     > let laMitadDe x = x / 2;;
     val laMitadDe : x:int -> int
    
  • Haciendo uso de forwar pipe (|>):

     >  elTripleDe 10 |> laMitadDe;;
     val it : int = 15
     > elTripleDe 40 |> laMitadDe
     val it : int = 60
    

    La mitad de 100 es 50, la mitad de 50 es 25, la mitad de 25 es 12:

     > laMitadDe 100 |> laMitadDe |> laMitadDe
     val it : int = 12
    
  • Otras expresiones:

     > [|3; 4; 5|].[0] |> fun x -> x > 0;;
     val it : bool = true
     > [|3; -4; 5|].[1] |> fun x -> x > 0;;
     val it : bool = false
     > let elTripleDelTripleDelPrimero (xs : int[]) =
           xs.[0] |>
           elTripleDe |>
           elTripleDe
     val elTripleDelTripleDelPrimero : xs:int [] -> int
     >  elTripleDelTripleDelPrimero [|6; 7; 8|];;
     val it : int = 54
    

Funciones de orden superior

F#, al igual que otros lenguajes de programación funcional, cuenta con algunas funciones de orden superior.