generics - usando - Funciones con tipos de parámetros genéricos
programacion generica (5)
Aquí hay otra forma de usar las comprobaciones del tipo de tiempo de ejecución ...
let sqrt_int<''a> (x:''a) : ''a = // ''
match box x with
| :? int as i -> downcast (i |> float |> sqrt |> int |> box)
| :? int64 as i -> downcast (i |> float |> sqrt |> int64 |> box)
| _ -> failwith "boo"
let a = sqrt_int 9
let b = sqrt_int 100L
let c = sqrt_int "foo" // boom
Estoy intentando descubrir cómo definir una función que funcione en múltiples tipos de parámetros (por ejemplo, int e int64). Según tengo entendido, la sobrecarga de funciones no es posible en F # (sin duda el compilador se queja). Tomemos por ejemplo la siguiente función.
let sqrt_int = function
| n:int -> int (sqrt (float n))
| n:int64 -> int64 (sqrt (float n))
El compilador, por supuesto, se queja de que la sintaxis no es válida (parece que no se admiten las restricciones de tipo en la coincidencia de patrones), aunque creo que esto ilustra lo que me gustaría lograr: una función que opera en varios tipos de parámetros y devuelve un valor de tipo. Tengo la sensación de que esto es posible en F # usando una combinación de tipos genéricos / inferencia de tipo / coincidencia de patrones, pero la sintaxis me ha eludido. También he intentado usar el:? operador (pruebas de tipo dinámico) y cuando las cláusulas en el bloque de coincidencia de patrones, pero esto todavía produce errores de todo tipo.
Como soy bastante nuevo en el lenguaje, es posible que intente hacer algo imposible aquí, así que por favor avíseme si hay una solución alternativa.
Esto funciona:
type T = T with
static member ($) (T, n:int ) = int (sqrt (float n))
static member ($) (T, n:int64) = int64 (sqrt (float n))
let inline sqrt_int (x:''t) :''t = T $ x
Utiliza restricciones estáticas y sobrecarga, lo que hace una búsqueda en tiempo de compilación sobre el tipo de argumento.
Las restricciones estáticas se generan automáticamente en presencia de un operador (operador $
en este caso) pero siempre se puede escribir a mano:
type T = T with
static member Sqr (T, n:int ) = int (sqrt (float n))
static member Sqr (T, n:int64) = int64 (sqrt (float n))
let inline sqrt_int (x:''N) :''N = ((^T or ^N) : (static member Sqr: ^T * ^N -> _) T, x)
Más sobre esto here .
La sobrecarga suele ser el cúmulo de idiomas de tipo inferencial (al menos cuando, como F #, el sistema de tipo no es lo suficientemente potente como para contener clases de tipo). Hay varias opciones que tiene en F #:
- Utilice la sobrecarga en métodos (miembros de un tipo), en cuyo caso la sobrecarga funciona de forma similar a otros lenguajes .Net (puede ad-hoc sobrecargar miembros, las llamadas provistas pueden distinguirse por el número / tipo de parámetros)
- Use "en línea", "^" y restricciones de miembros estáticos para la sobrecarga ad-hoc en las funciones (esto es lo que la mayoría de los diversos operadores matemáticos que necesitan trabajar en int / float / etc .; la sintaxis aquí es rara, esto es poco utilizado, aparte de la biblioteca F #)
- Simule clases de tipos pasando un parámetro adicional de diccionario de operaciones (esto es lo que INumeric hace en una de las bibliotecas F # PowerPack para generalizar varios algoritmos matemáticos para tipos arbitrarios definidos por el usuario)
- Vuelva a la tipificación dinámica (pase un parámetro ''obj'', realice una prueba dinámica de tipo, ejecute una excepción en tiempo de ejecución para el tipo incorrecto)
Para su ejemplo particular, probablemente solo usaría la sobrecarga de métodos:
type MathOps =
static member sqrt_int(x:int) = x |> float |> sqrt |> int
static member sqrt_int(x:int64) = x |> float |> sqrt |> int64
let x = MathOps.sqrt_int 9
let y = MathOps.sqrt_int 100L
No quitar las respuestas correctas ya provistas, pero de hecho puede usar restricciones de tipo en la coincidencia de patrones. La sintaxis es:
| :? type ->
O si desea combinar el control de tipos y el casting:
| :? type as foo ->
Si, esto se puede hacer. Eche un vistazo a este hilo de HubFS .
En este caso, la solución sería:
let inline retype (x:''a) : ''b = (# "" x : ''b #)
let inline sqrt_int (n:''a) = retype (sqrt (float n)) : ''a
Advertencia : no hay verificación de tipos en tiempo de compilación. Es decir, sqrt_int "blabla"
compila bien, pero obtendrá FormatException en tiempo de ejecución.