Compartir vía


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 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 del 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érico.

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 mayor que (>) es genérico. El operador mayor que 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, no se puede generalizar ese tipo de parámetro.

Dado max que es genérico, se puede usar con tipos como int, float, etc., 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 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 mayor que. Por lo tanto, también puede 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 tampoco es generalizable. El mensaje de error de este problema hace referencia a esta restricción sobre la generalización automática de los valores como 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 generalizarlo o cuando se omite accidentalmente información de tipo suficiente en una construcción no genérica. La solución al error de restricción de valores 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 usa una construcción nogeneralizable para definir una función genérica, como una composición de función o argumentos de función curriados aplicados incompletamente, intente volver a escribir la función como una definición de función normal.

  • Si el problema es una expresión demasiado compleja para generalizarse, consíñala 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 muestran cada uno de estos escenarios.

Caso 1: Una expresión demasiado compleja. En este ejemplo, la lista counter está pensada para 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: Usar una construcción nogeneralizable 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 valores.

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 indica a continuación:

let intLists = arrayOf10Lists<int>
let floatLists = arrayOf10Lists<float>

Consulte también