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 = 20Compilado 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.exeofsharpc) generará un archivo binario ejecutable mientras que el interprete (fsi.exeofsharpi) 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.0Evaluació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 
0hasta255. Sufijo: uy - sbyte: desde 
-128hasta127. Sufijo: y - int16: desde 
-32768hasta32767. Sufijo: s - uint16: desde 
0hasta65535. Sufijo: us - int: desde 
-2,147,483,648hasta2,147,483,647. - uint32: desde 
0hasta4,294,967,295. Sufijo: u - int64: desde 
-9,223,372,036,854,775,808hasta9,223,372,036,854,775,807. Sufijo: L - uint64: desde 
0hasta18,446,744,073,709,551,615. Sufijo: UL - float32: de 32-bits (desde 
-3.402823e38hasta3.402823e38) Sufijo: f - float: de 64-bits (desde 
-1.79769313486232e308hasta1.79769313486232e308) 
- byte: desde 
 - Booleanos:
- bool: 
trueyfalse. 
 - bool: 
 - Caracteres:
- char: 
'a','\\','@',' ','\t'. 
 - char: 
 - Cadenas de caracteres:
- string: 
"Hola mundo!","a","\\","Hola\t\t\tmundo\t\t\t!". 
 - string: 
 
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 = 80Arreglo 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íneaPara 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.

El valor de eso tiene tipo byte con signo (signed byte) y es el -12:
> -12y;; val it : sbyte = -12yEl valor de eso tiene tipo byte sin signo (unsigned byte) y es el 130:
> 130uy;; val it : byte = 130uyEl 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 = trueEl valor de eso tiene tipo booleano (bool) y es false:
> not true;; val it : bool = falseEl valor de eso tiene tipo carácter (char) y es el carácter 1:
> '1';; val it : char = '1'
Tuplas

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>

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:

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 = 2uyFunció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 = 234Funció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 = 98Funció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.0Funció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 = 10Funció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 intFunció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 = trueFunció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 * intAplicació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.25Misma 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.25Misma 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.25Esta ú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.0Misma función:
> (fun (x:float, y:float) -> x * y)(2.5, 2.0) val it : float = 5.0Funció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.0Funció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.0La 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ámetroxde la primera función.> (fun x y z -> 1.0 * x) 2.5 2.0 3.0;; val it : float = 2.5F# 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 patternAunque 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.0Funció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 -> floatO bien:
> let areaDeUnTriangulo baseDelTriangulo alturaDelTriangulo = (baseDelTriangulo * alturaDelTriangulo) / 2.0 val areaDeUnTriangulo : baseDelTriangulo:float -> alturaDelTriangulo:float -> floatAplicació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) -> 'aAplicaciones:
> 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 dexque tiene tipo alfa a una función que va degque va de alfa a beta a beta:> let f x = fun g -> g x;; val f : x:'a -> g:('a -> 'b) -> 'bAplicaciones:
> (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 -> intHaciendo uso de forwar pipe (
|>):> elTripleDe 10 |> laMitadDe;; val it : int = 15 > elTripleDe 40 |> laMitadDe val it : int = 60La mitad de 100 es 50, la mitad de 50 es 25, la mitad de 25 es 12:
> laMitadDe 100 |> laMitadDe |> laMitadDe val it : int = 12Otras 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.