Generalización automática (F#)
F# utiliza la inferencia de tipos para evaluar los tipos de funciones y expresiones.Este tema describe cómo F# generaliza automáticamente los argumentos y tipos de funciones para que funcionen con varios tipos cuando sea posible.
Generalización automática
Al realizar la inferencia de tipos con una función, el compilador de F# 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 del tipo específico de ese parámetro.En caso negativo, la inferencia de tipos da como resultado que el tipo es genérico.
En el ejemplo de código siguiente se muestra una función que es genérica según la inferencia de tipos realizada por el compilador.
let max a b = if a > b then a else b
Según la inferencia de tipos, el tipo es '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 mayor que (>) es genérico en sí mismo.El operador mayor que tiene la signatura 'a -> 'a -> bool.Dado que no todos los operadores son genéricos, si el código de una función utiliza un tipo de parámetro junto con una función o un operador que no es genérico, ese tipo de parámetro no se puede generalizar.
Dado que max es genérico, se puede utilizar con tipos como int, float, etc., tal y como se muestra en los ejemplos siguientes.
let biggestFloat = max 2.0 3.0
let biggestInt = max 2 3
Sin embargo, los dos argumentos deben ser del mismo tipo.La signatura es 'a -> 'a -> 'a, no 'a -> 'b -> 'a.Por consiguiente, el código siguiente genera un error ya que los tipos no coinciden.
// Error: type mismatch.
let biggestIntFloat = max 2.0 3
La función max también puede usarse con cualquier tipo que admita el operador mayor que.En consecuencia, también se puede utilizar con una cadena, tal y como se muestra en el código siguiente.
let testString = max "cab" "cat"
Restricción de valor
El compilador realiza la generalización automática únicamente en las definiciones de función completas que tienen argumentos explícitos y en los valores inmutables simples.
Esto significa que el compilador genera un error si se intenta compilar código que no está lo bastante restringido para ser de un tipo específico, pero tampoco se puede generalizar.El mensaje de error de este problema se refiere a esta restricción de la generalización automática de valores con el término restricción de valor.
Normalmente, el error de restricción de valor se produce bien cuando se desea que una construcción sea genérica pero el compilador no dispone de información suficiente para generalizarla, o bien cuando se omite involuntariamente la información de tipos necesaria 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 limitar mejor el problema de inferencia de tipos, de una de las maneras siguientes:
Restringir 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 se refiere al uso de una construcción que no se puede generalizar para definir una función genérica (como una composición de funciones o como argumentos de funciones currificadas que no se han aplicado completamente), intente escribir la función de nuevo mediante una definición de función ordinaria.
Si el problema reside en una expresión de valor que es demasiado compleja para generalizarla, conviértala en una función agregando un parámetro no utilizado adicional.
Agregar parámetros de tipo genérico explícitos.Esta opción se usa en raras ocasiones.
En los ejemplos de código siguientes se muestran todos estos casos.
Caso 1: expresión demasiado compleja.En este ejemplo, la lista counter pretende ser int option ref, pero no se define como un valor inmutable simple.
let counter = ref None
// Adding a type annotation fixes the problem:
let counter : int option ref = ref None
Caso 2: uso de una construcción no generalizable para definir una función genérica.En este ejemplo, la construcción es no generalizable 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: adición de un parámetro no utilizado adicional.Dado que esta expresión no es lo bastante simple para la generalización, el compilador emite un 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: adición de 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 utilizar para crear valores de varios tipos, por ejemplo como sigue:
let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>