Generalización automática
F# usa la inferencia de tipos para evaluar los tipos de funciones y expresiones. En este tema se describe cómo F# generaliza automáticamente los argumentos y los tipos de funciones para que funcionen con varios tipos cuando esto sea posible.
Generalización automática
El compilador de F#, cuando realiza la inferencia de tipos en una función, determina si un parámetro determinado puede ser genérico. El compilador examina cada parámetro y determina si la función tiene una dependencia en el tipo específico de ese parámetro. Si no es así, se deduce que el tipo es genérico.
En el ejemplo de código siguiente se muestra una función que el compilador deduce que es genérica.
let max a b = if a > b then a else b
El tipo se deduce como 'a -> 'a -> 'a .
El tipo indica que se trata de una función que toma dos argumentos del mismo tipo desconocido y devuelve un valor de ese mismo tipo. Una de las razones por las que la función anterior puede ser genérica es que el operador greater-than ( > ) es en sí mismo genérico. El operador greater-than tiene la firma 'a -> 'a -> bool . No todos los operadores son genéricos y, si el código de una función usa un tipo de parámetro junto con una función o un operador no genéricos, ese tipo de parámetro no se puede generalizar.
Dado que es genérico, se puede usar con tipos como , , , y así sucesivamente, como se max muestra en los ejemplos int float siguientes.
let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3
Sin embargo, los dos argumentos deben ser del mismo tipo. La firma es 'a -> 'a -> 'a , no 'a -> 'b -> 'a . Por lo tanto, el código siguiente genera un error porque los tipos no coinciden.
// Error: type mismatch.
let biggestIntFloat = max 2.0 3
La max función también funciona con cualquier tipo que admita el operador greater-than. Por lo tanto, también podría usarlo en una cadena, como se muestra en el código siguiente.
let testString = max "cab" "cat"
Restricción de valores
El compilador realiza la generalización automática solo en definiciones de función completas que tienen argumentos explícitos y en valores inmutables simples.
Esto significa que el compilador emite un error si intenta compilar código que no está suficientemente restringido para ser un tipo específico, pero que tampoco es generalizable. El mensaje de error de este problema hace referencia a esta restricción de generalización automática para los valores como la restricción de valor.
Normalmente, el error de restricción de valor se produce cuando se quiere que una construcción sea genérica pero el compilador no tiene información suficiente para generalizarla, o cuando se omite involuntaramente información de tipo suficiente en una construcción no genérica. La solución al error de restricción de valor es proporcionar información más explícita para restringir más completamente el problema de inferencia de tipos, de una de las maneras siguientes:
Restrinja un tipo para que no sea genérico agregando una anotación de tipo explícita a un valor o parámetro.
Si el problema está usando una construcción no generable para definir una función genérica, como una composición de función o argumentos de función consultadas aplicadas incompletamente, intente reescribir la función como una definición de función normal.
Si el problema es una expresión demasiado compleja para generalizarla, considéla en una función agregando un parámetro adicional sin usar.
Agregue parámetros de tipo genérico explícitos. Esta opción rara vez se usa.
Los ejemplos de código siguientes ilustran cada uno de estos escenarios.
Caso 1: Una expresión demasiado compleja. En este ejemplo, la lista está pensada para ser , pero no se define como counter int option ref un valor inmutable simple.
let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None
Caso 2: Usar una construcción no generable para definir una función genérica. En este ejemplo, la construcción no se puede generar porque implica la aplicación parcial de argumentos de función.
let maxhash = max << hash
// The following is acceptable because the argument for maxhash is explicit:
let maxhash obj = (max << hash) obj
Caso 3: Agregar un parámetro adicional sin usar. Dado que esta expresión no es lo suficientemente sencilla para la generalización, el compilador emite el error de restricción de valor.
let emptyList10 = Array.create 10 []
// Adding an extra (unused) parameter makes it a function, which is generalizable.
let emptyList10 () = Array.create 10 []
Caso 4: Agregar parámetros de tipo.
let arrayOf10Lists = Array.create 10 []
// Adding a type parameter and type annotation lets you write a generic value.
let arrayOf10Lists<'T> = Array.create 10 ([]:'T list)
En el último caso, el valor se convierte en una función de tipo, que se puede usar para crear valores de muchos tipos diferentes, por ejemplo, como se muestra a continuación:
let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>