Polimorfismo y argumentos implícitos
Leyendo un poco el Reynolds me doy cuenta que no estamos tratando el polimorfismo para nada igual. Lo que se presenta en el capítulo 17 del famoso libro es básicamente una introducción teórica de System F (es decir, bastante funcional la cosa) que resumo rápidamente a continuación.
Partiendo de la base de un lenguaje con tipos, agrega al lenguaje de los tipos las variables de tipo y un binder a nivel de tipos
\langle type \rangle ::= ... \, | \, \langle tvar \rangle \, | \, \lambda \langle tvar \rangle . \langle type \rangle
además agregamos a las expresiones el binder de tipos y la aplicación de tipos (sí, también en las expresiones)
\langle expr \rangle ::= ... \, | \, \Lambda \langle tvar \rangle . \langle expr \rangle \, | \, \langle expr \rangle [ \langle type \rangle ]
Está bueno este ejemplito como para entender un poco la aplicación de un tipo como argumento
Notar que el primer argumento de la función double es un tipo.
Nosotros
Una primera idea que tenía en mente era copiar bastante de esta idea con la salvedad de que los argumentos de los tipos bien podrían ser implícitos, sospecho que siempre vamos a poder "inferir" este tipo ya que nuestro lenguaje no tiene alto orden, es decir que son todos tipos simples o estructurados; estaría bueno pensar bien esto. Por ejemplo si tenemos el procedimiento:
proc P (in/out a : array[1..n] of T)
...
end proc
Cada vez que apliquemos este proc, por ejemplo P(a)
con a : array[1..5] of int
internamente vamos a tener una representación Proc "P" [int] [a]
. Donde [int]
representa la lista de argumentos implicitos para este ejemplo.
Por supuesto esto no termina acá, porque del ejemplo reciente podemos preguntarnos ¿qué pasa con n
? Es bien importante porque muy probablemente usemos a n
en el cuerpo de la definición del procedimiento. Una posible refinada razonable entonces es tener dos tipos de argumentos implícitos: de tipos y de valores. Siguiendo con el ejemplo entonces la aplicación quedaría representada internamente como Proc "P" [int] [5] [a]
.
Una segunda idea es no complicar la sintaxis con argumentos implícitos usando la suposición de que siempre vamos a poder inferirlos de información "local a la aplicación".
Ejemplo
Como chequeamos y ejecutamos este código:
// procedimiento inicializador bastante universal
proc init (in/out a : array[1..n] of T, in e : T)
for i = 1 to n do
a[i] := e
od
end proc
proc sum1 (in/out a : array[1..n] of int)
for i = 1 to n do
a[i] := a[i] + 1
od
end proc
proc main ()
var a : array[1..5] of int
init(a, 0)
sum1(a)
end proc