La manera más obvia:
type Num = {
def +(a: Num): Num
def *(a: Num): Num
}
def pyth[A <: Num](a: A, b: A)(sqrt: A=>A) = sqrt(a * a + b * b)
// usage
pyth(3, 4)(Math.sqrt)
Esto es horrible por muchas razones. Primero, tenemos el problema del tipo recursivo, Num
. Esto solo está permitido si compila este código con la opción -Xrecursive
establecida en un valor entero (5 es probablemente más que suficiente para los números). Segundo, el tipo Num
es estructural, lo que significa que cualquier uso de los miembros que define se compilará en las correspondientes invocaciones reflexivas. En pocas palabras, esta versión de pyth
es obscenamente ineficiente, corriendo en el orden de varios cien mil veces más lento que una implementación convencional. Sin embargo, no hay forma de evitar el tipo de estructura si desea definir pyth
para cualquier tipo que defina +
, *
y para la cual exista una función sqrt
.
Finalmente, llegamos a la cuestión más fundamental: es demasiado complicado. ¿Por qué molestarse en implementar la función de esta manera? Hablando en términos prácticos, los únicos tipos a los que tendrá que aplicar son los números reales de Scala. Por lo tanto, es más fácil simplemente hacer lo siguiente:
def pyth(a: Double, b: Double) = Math.sqrt(a * a + b * b)
¡Todos los problemas resueltos! Esta función se puede utilizar en los valores del tipo Double
, Int
, Float
, incluso los impares como Short
gracias a las maravillas de la conversión implícita. Si bien es cierto que esta función es técnicamente menos flexible que nuestra versión de tipo estructural, es ampliamente más eficiente y eminentemente más legible. Es posible que hayamos perdido la capacidad de calcular el teorema de Pythagrean para tipos imprevistos que definen +
y *
, pero no creo que vaya a perder esa capacidad.
Y ahora sé por qué los tipos estructurales no me permiten hacerlo: http://article.gmane.org/gmane.comp.lang.scala/7013 –