Buscar..
Introducción a los tipos
Los tipos pueden representar varios tipos de cosas. Puede ser un solo dato, un conjunto de datos o una función.
En F #, podemos agrupar los tipos en dos categorías:
Tipos f #:
// Functions let a = fun c -> c // Tuples let b = (0, "Foo") // Unit type let c = ignore // Records type r = { Name : string; Age : int } let d = { Name = "Foo"; Age = 10 } // Discriminated Unions type du = | Foo | Bar let e = Bar // List and seq let f = [ 0..10 ] let g = seq { 0..10 } // Aliases type MyAlias = string
Tipos .NET
- Tipo incorporado (int, bool, string, ...)
- Clases, Estructuras e Interfaces
- Delegados
- Arrays
Escriba las abreviaturas
Las abreviaturas de tipo le permiten crear alias en los tipos existentes para darles un sentido más significativo.
// Name is an alias for a string
type Name = string
// PhoneNumber is an alias for a string
type PhoneNumber = string
Entonces puedes usar el alias como cualquier otro tipo:
// Create a record type with the alias
type Contact = {
Name : Name
Phone : PhoneNumber }
// Create a record instance
// We can assign a string since Name and PhoneNumber are just aliases on string type
let c = {
Name = "Foo"
Phone = "00 000 000" }
printfn "%A" c
// Output
// {Name = "Foo";
// Phone = "00 000 000";}
Tenga cuidado, los alias no comprueban la consistencia del tipo. Esto significa que dos alias que se dirigen al mismo tipo pueden asignarse entre sí:
let c = {
Name = "Foo"
Phone = "00 000 000" }
let d = {
Name = c.Phone
Phone = c.Name }
printfn "%A" d
// Output
// {Name = "00 000 000";
// Phone = "Foo";}
Los tipos se crean en F # usando la palabra clave de tipo
F#
usa la palabra clave de type
para crear diferentes tipos de tipos.
- Tipo de alias
- Sindicatos discriminados.
- Tipos de registro
- Tipos de interfaz
- Tipos de clase
- Tipos de struct
Ejemplos con código C#
equivalente cuando sea posible:
// Equivalent C#:
// using IntAliasType = System.Int32;
type IntAliasType = int // As in C# this doesn't create a new type, merely an alias
type DiscriminatedUnionType =
| FirstCase
| SecondCase of int*string
member x.SomeProperty = // We can add members to DU:s
match x with
| FirstCase -> 0
| SecondCase (i, _) -> i
type RecordType =
{
Id : int
Name : string
}
static member New id name : RecordType = // We can add members to records
{ Id = id; Name = name } // { ... } syntax used to create records
// Equivalent C#:
// interface InterfaceType
// {
// int Id { get; }
// string Name { get; }
// int Increment (int i);
// }
type InterfaceType =
interface // In order to create an interface type, can also use [<Interface>] attribute
abstract member Id : int
abstract member Name : string
abstract member Increment : int -> int
end
// Equivalent C#:
// class ClassType : InterfaceType
// {
// static int increment (int i)
// {
// return i + 1;
// }
//
// public ClassType (int id, string name)
// {
// Id = id ;
// Name = name ;
// }
//
// public int Id { get; private set; }
// public string Name { get; private set; }
// public int Increment (int i)
// {
// return increment (i);
// }
// }
type ClassType (id : int, name : string) = // a class type requires a primary constructor
let increment i = i + 1 // Private helper functions
interface InterfaceType with // Implements InterfaceType
member x.Id = id
member x.Name = name
member x.Increment i = increment i
// Equivalent C#:
// class SubClassType : ClassType
// {
// public SubClassType (int id, string name) : base(id, name)
// {
// }
// }
type SubClassType (id : int, name : string) =
inherit ClassType (id, name) // Inherits ClassType
// Equivalent C#:
// struct StructType
// {
// public StructType (int id)
// {
// Id = id;
// }
//
// public int Id { get; private set; }
// }
type StructType (id : int) =
struct // In order create a struct type, can also use [<Struct>] attribute
member x.Id = id
end
Inferencia de tipos
Reconocimiento
Este ejemplo es una adaptación de este artículo sobre la inferencia de tipos.
¿Qué es el tipo de inferencia?
Inferencia de tipos es el mecanismo que permite al compilador deducir qué tipos se usan y dónde. Este mecanismo se basa en un algoritmo a menudo llamado "Hindley-Milner" o "HM". Vea a continuación algunas de las reglas para determinar los tipos de valores simples y de función:
- Mira los literales
- Mira las funciones y otros valores con los que algo interactúa.
- Mira cualquier restricción de tipo explícito
- Si no hay restricciones en ninguna parte, generalice automáticamente a tipos genéricos
Mira los literales
El compilador puede deducir tipos mirando los literales. Si el literal es un int y usted le agrega una "x", entonces "x" también debe ser un int. Pero si el literal es un flotador y le está agregando una "x", entonces "x" también debe ser un flotador.
Aquí hay unos ejemplos:
let inferInt x = x + 1
let inferFloat x = x + 1.0
let inferDecimal x = x + 1m // m suffix means decimal
let inferSByte x = x + 1y // y suffix means signed byte
let inferChar x = x + 'a' // a char
let inferString x = x + "my string"
Mira las funciones y otros valores con los que interactúa.
Si no hay literales en ninguna parte, el compilador intenta resolver los tipos analizando las funciones y otros valores con los que interactúan.
let inferInt x = x + 1
let inferIndirectInt x = inferInt x //deduce that x is an int
let inferFloat x = x + 1.0
let inferIndirectFloat x = inferFloat x //deduce that x is a float
let x = 1
let y = x //deduce that y is also an int
Mira cualquier restricción de tipo explícito o anotaciones
Si se especifican restricciones de tipo explícitas o anotaciones, entonces el compilador las usará.
let inferInt2 (x:int) = x // Take int as parameter
let inferIndirectInt2 x = inferInt2 x // Deduce from previous that x is int
let inferFloat2 (x:float) = x // Take float as parameter
let inferIndirectFloat2 x = inferFloat2 x // Deduce from previous that x is float
Generalización automática
Si después de todo esto, no se encuentran restricciones, el compilador simplemente hace que los tipos sean genéricos.
let inferGeneric x = x
let inferIndirectGeneric x = inferGeneric x
let inferIndirectGenericAgain x = (inferIndirectGeneric x).ToString()
Cosas que pueden ir mal con la inferencia de tipos.
La inferencia de tipos no es perfecta, por desgracia. A veces el compilador simplemente no tiene ni idea de qué hacer. Nuevamente, entender lo que está sucediendo realmente te ayudará a mantener la calma en lugar de querer matar al compilador. Estas son algunas de las principales razones de los errores de tipo:
- Declaraciones fuera de orden
- No hay suficiente información
- Métodos sobrecargados
Declaraciones fuera de orden
Una regla básica es que debe declarar las funciones antes de que se utilicen.
Este código falla:
let square2 x = square x // fails: square not defined
let square x = x * x
Pero esto está bien:
let square x = x * x
let square2 x = square x // square already defined earlier
Declaraciones recursivas o simultáneas.
Una variante del problema "fuera de servicio" ocurre con funciones recursivas o definiciones que tienen que referirse entre sí. Ninguna cantidad de reordenamiento ayudará en este caso, necesitamos usar palabras clave adicionales para ayudar al compilador.
Cuando se está compilando una función, el identificador de la función no está disponible para el cuerpo. Entonces, si define una función recursiva simple, obtendrá un error del compilador. La solución es agregar la palabra clave "rec" como parte de la definición de la función. Por ejemplo:
// the compiler does not know what "fib" means
let fib n =
if n <= 2 then 1
else fib (n - 1) + fib (n - 2)
// error FS0039: The value or constructor 'fib' is not defined
Aquí está la versión fija con "rec fib" agregada para indicar que es recursiva:
let rec fib n = // LET REC rather than LET
if n <= 2 then 1
else fib (n - 1) + fib (n - 2)
No hay suficiente información
A veces, el compilador simplemente no tiene suficiente información para determinar un tipo. En el siguiente ejemplo, el compilador no sabe en qué tipo se supone que funciona el método Length. Pero tampoco puede hacer que sea genérico, así que se queja.
let stringLength s = s.Length
// error FS0072: Lookup on object of indeterminate type
// based on information prior to this program point.
// A type annotation may be needed ...
Este tipo de error se puede solucionar con anotaciones explícitas.
let stringLength (s:string) = s.Length
Métodos sobrecargados
Al llamar a una clase o método externo en .NET, a menudo recibirá errores debido a la sobrecarga.
En muchos casos, como el siguiente ejemplo de concat, tendrá que anotar explícitamente los parámetros de la función externa para que el compilador sepa a qué método sobrecargado llamar.
let concat x = System.String.Concat(x) //fails
let concat (x:string) = System.String.Concat(x) //works
let concat x = System.String.Concat(x:string) //works
A veces, los métodos sobrecargados tienen diferentes nombres de argumento, en cuyo caso también puede dar una pista al compilador al nombrar los argumentos. Aquí hay un ejemplo para el constructor StreamReader.
let makeStreamReader x = new System.IO.StreamReader(x) //fails
let makeStreamReader x = new System.IO.StreamReader(path=x) //works