Primero una explicación terminológica: las posiciones negativas y positivas provienen de la lógica. Son alrededor de una asimetría en conectores lógicos: en al A se comporta de manera diferente de B . Algo similar sucede en la teoría de categorías, donde decimos contravariante y covariante en lugar de negativo y positivo, respectivamente. En física, hablan de cantidades que se comportan "covariablemente" y "contrariamente, también. Así que este es un fenómeno muy general. Un programador podría pensar en ellas como" entrada "y" salida ".A ⇒ BUNsi
Ahora en tipos de datos inductivos.
Piensa en un tipo de datos inductiva como una especie de estructura algebraica: constructores son las operaciones que tienen elementos de T como argumentos y producen nuevos elementos de T . Esto es muy similar al álgebra ordinaria: la suma toma dos números y produce un número.TTT
En álgebra, es costumbre que una operación tome un número finito de argumentos, y en la mayoría de los casos toma cero (constante), uno (unario) o dos (binarios) argumentos. Es conveniente generalizar esto para constructores de tipos de datos. Supongamos que c
es un constructor para un tipo de datos T
:
- si
c
es una constante, podemos pensar en ella como una función unit -> T
, o de manera equivalente (empty -> T) -> T
,
- si
c
es unario, podemos considerarlo como una función T -> T
, o de manera equivalente (unit -> T) -> T
,
- si
c
es binario, podemos considerarlo como una función T -> T -> T
, o de manera equivalente T * T -> T
o equivalente (bool -> T) -> T
,
- Si quisiéramos un constructor
c
que tomara siete argumentos, podríamos verlo como una función (seven -> T) -> T
donde seven
hay algún tipo previamente definido con siete elementos.
- También podemos tener un constructor
c
que tome innumerables argumentos, que sería una función (nat -> T) -> T
.
Estos ejemplos muestran que la forma general de un constructor debe ser
c : (A -> T) -> T
donde llamamos a A
la aridad de c
y pensamos que c
es un constructor que toma A
-muchos argumentos de tipo T
para producir un elemento de T
.
Aquí hay algo muy importante: las aridades deben definirse antes de que las definamos T
, o de lo contrario no podemos saber qué deben hacer los constructores. Si alguien intenta tener un constructor
broken: (T -> T) -> T
entonces la pregunta "¿cuántos argumentos broken
lleva?" No tiene una buena respuesta. Puede intentar responderlo con "se necesitan T
muchos argumentos", pero eso no servirá, porque T
aún no está definido. Podríamos tratar de salir del cunundrum usando una teoría elegante de punto fijo para encontrar un tipo T
y una función inyectiva (T -> T) -> T
, y tendríamos éxito, pero también romperíamos el principio de inducción en T
el camino. Entonces, es una mala idea intentar tal cosa.
λvλ ⋅ vc
B
c : B * (A -> T) -> T
De hecho, muchos constructores se pueden reescribir de esta forma, pero no todos, necesitamos un paso más, es decir, debemos permitir A
que dependerá de B
:
c : (∑ (x : B), A x -> T) -> T
Esta es la forma final de un constructor para un tipo inductivo. También es precisamente lo que son los tipos W. ¡La forma es tan general que solo necesitamos un solo constructor c
! De hecho, si tenemos dos de ellos
d' : (∑ (x : B'), A' x -> T) -> T
d'' : (∑ (x : B''), A'' x -> T) -> T
entonces podemos combinarlos en uno
d : (∑ (x : B), A x -> T) -> T
dónde
B := B' + B''
A(inl x) := A' x
A(inr x) := A'' x
Por cierto, si cambiamos la forma general, vemos que es equivalente a
c : ∏ (x : B), ((A x -> T) -> T)
que está más cerca de lo que la gente realmente escribe en asistentes de prueba. Los asistentes de prueba nos permiten escribir los constructores de manera conveniente, pero son equivalentes a la forma general anterior (¡ejercicio!).
A
y explotar la pila eventualmente (en lenguajes basados en pila).