Estoy aprendiendo programación en ML (OCaml), y antes pregunté sobre las funciones de tipo ML'a -> 'b
. Ahora he estado experimentando un poco con funciones de tipo 'a list -> 'b list
. Hay algunos ejemplos simples obvios:
let rec loop l = loop l
let return_empty l = []
let rec loop_if_not_empty = function [] -> []
| l -> loop_if_not_empty l
Lo que no puedo entender es cómo hacer una función que haga algo más que devolver la lista vacía o el bucle (sin usar ninguna función de biblioteca). Se puede hacer esto? ¿Hay alguna manera de devolver listas no vacías?
Editar: Sí, si tengo una función de tipo 'a -> 'b
, entonces puedo hacer otra, o una función de tipo 'a list -> 'b list
, pero lo que me pregunto aquí es cómo hacer la primera.
programming-languages
typing
functional-programming
Gilles 'SO- deja de ser malvado'
fuente
fuente
Respuestas:
Bueno, algo conocido como parametricidad nos dice que si consideramos el subconjunto puro de ML (es decir, sin recursión infinita
ref
y todas esas cosas raras), no hay forma de definir una función con este tipo que no sea la que devuelve el vacío lista.Todo esto comenzó con el artículo de Wadler "¡ Teoremas gratis! ". Este artículo, básicamente, nos dice dos cosas:
ref
y todas esas cosas raras) satisface esas condiciones.Desde el teorema Parametricity sabemos que si tenemos una función
f : 'a list -> 'b list
, a continuación, para todos'a
,'b
,'c
,'d
y para todas las funcionesg : 'a -> 'c
,h : 'b -> 'd
tenemos:(Nota,
f
a la izquierda tiene tipo'a list -> 'b list
yf
a la derecha es'c list -> 'd list
).Somos libres de elegir lo
g
que queramos, así que deja'a = 'c
yg = id
. Ahora desdemap id = id
(fácil de probar por inducción en la definición demap
), tenemos:Ahora deja
'b = 'd = bool
yh = not
. Asumamos que para algunoszs : bool list
sucede esof zs ≠ [] : bool list
. Está claro quemap not ∘ f = f
no se sostiene, porqueSi el primer elemento de la lista de la derecha es
true
, entonces, a la izquierda, el primer elemento esfalse
y viceversa.Esto significa que nuestra suposición es incorrecta y
f zs = []
. ¿Terminamos? No.Asumimos que
'b
esbool
. Hemos demostrado que cuandof
se invoca con typef : 'a list -> bool list
para any'a
,f
siempre debe devolver la lista vacía. ¿Puede ser que cuando llamamosf
yaf : 'a list -> unit list
que devuelve algo diferente? Nuestra intuición nos dice que esto no tiene sentido: simplemente no podemos escribir en ML pura una función que siempre devuelve la lista vacía cuando queremos que nos dé una lista de booleanos y de lo contrario podría devolver una lista no vacía. Pero esto no es una prueba.Lo que queremos decir es que
f
es uniforme : si siempre devuelve la lista vacía parabool list
, entonces tiene que devolver la lista vacía paraunit list
y, en general, cualquier'a list
. Esto es exactamente de lo que trata el segundo punto en la lista de viñetas al comienzo de mi respuesta.El artículo nos dice que en ML
f
debe tomar valores relacionados con valores relacionados . No voy a entrar en detalles sobre las relaciones, es suficiente decir que las listas están relacionadas si y solo si tienen la misma longitud y sus elementos están relacionados por pares (es decir,[x_1, x_2, ..., x_m]
y[y_1, y_2, ..., y_n]
están relacionados si y solo sim = n
yx_1
están relacionados cony_1
yx_2
está relacionado cony_2
y así sucesivamente). Y la parte divertida es, en nuestro caso, dado quef
es polimórfico, ¡ podemos definir cualquier relación en los elementos de las listas!Vamos a recoger cualquier
'a
,'b
y miranf : 'a list -> 'b list
. Ahora miraf : 'a list -> bool list
; Ya hemos demostrado que en este casof
siempre devuelve la lista vacía. Ahora postulamos que todos los elementos de'a
están relacionados entre sí (recuerde, podemos elegir cualquier relación que queramos), esto implica que cualquierazs : 'a list
está relacionado con sí mismo. Como sabemos,f
toma valores relacionados con valores relacionados, esto significa quef zs : 'b list
está relacionado conf zs : bool list
, pero la segunda lista tiene una longitud igual a cero, y dado que la primera está relacionada con ella, también está vacía.Para completar, mencionaré que hay una sección sobre el impacto de la recursividad general (posible no terminación) en el documento original de Wadler, y también hay un documento que explora teoremas libres en presencia de
seq
.fuente
g
yh
en este caso) ir directamente con las relaciones generales a medida ...Volvamos a los objetos más simples: no puede construir un objeto de tipo adecuado
'a
porque entonces significaría que este objetox
se puede usar donde sea'a
que encaje. Y eso significa en todas partes: como un entero, una matriz, incluso una función. Por ejemplo, eso significaría que puede hacer cosas comox+2
,x.(1)
y(x 5)
. Los tipos existen exactamente para evitar esto.Esta es la misma idea que se aplica con una función de tipo
'a -> 'b
, pero hay algunos casos en los que este tipo puede existir: cuando la función nunca devuelve un objeto de tipo'b
: cuando se repite o se genera una excepción.Esto también se aplica a las funciones que devuelven una lista. Si su función es de tipo
t -> 'b list
y crea un objeto de tipot
y lo aplica a esta función, eso significa que si accede con éxito a un elemento de esta lista, accederá a un objeto que tenga todos los tipos. Por lo tanto, no puede acceder a ningún elemento de la lista: la lista está vacía o ... no hay lista.Sin embargo, el tipo
'a list -> 'b list
aparece en los ejercicios habituales, pero eso es solo cuando ya tiene una función de tipo'a -> 'b
:Pero probablemente conozcas este.
fuente
'a -> 'b
o'a list -> 'b list
, pero esa no es una observación tan interesante. De hecho, voy a editar la pregunta para dejar en claro que esto no es de lo que se preguntaba la programación de aprendizaje para estudiantes más jóvenes.f : 'a list -> 'b list
yt
tal quef t <> []
entonces este programa escribirá-cheque, pero puede hacer camino peor que lanzar una excepción:let y = List.hd (f t) in (y y) (y + y.(0) + y.(0).(0))
.Teorema de parametricidad de los "Teoremas gratis!" El artículo nos dice que los términos de ML tienen una propiedad muy especial: si vemos el tipo de un término como una relación con los valores de este tipo, entonces el valor de este término estará relacionado con sí mismo. Aquí se explica cómo ver los tipos como relaciones:
'a -> 'b
corresponde a la relación definida al decir que dos funciones están relacionadas si toman valores relacionados con valores relacionados (asumiendo'a
y'b
corresponden a algunas relaciones).'a list
corresponde a la relación definida al decir que dos listas están relacionadas si tienen la misma longitud y sus elementos coincidentes están relacionados (suponiendo que'a
corresponda a alguna relación).Aquí hay un ejemplo. Supongamos que tenemos un términoUNA1 y UNA2 , elige absolutamente cualquier relación UNA entre elementos de este tipo, y si tomamos alguno una1:UNA1 y una2:UNA2 , de modo que estén relacionados de acuerdo con UNA , entonces foouna1 y foouna2 también estará relacionado de acuerdo a UNA :
foo : 'a -> 'a
. El teorema de la parametricidad dice quefoo
está relacionado con sí mismo. Lo que esto significa es que podemos elegir dos tipos, digamos,Ahora si tomamos la relaciónUNA no ser una relación arbitraria, sino una función F:UNA1→UNA2 , lo anterior se convierte en:
o, en otras palabras:
que es exactamente el teorema libre para la
id
función:f . id = id . f
.Si realiza pasos similares para su funciónUNA1 y UNA2 , cualquier relación UNA entre sus elementos, cualesquiera dos tipos si1 y si2 , cualquier relación si entre sus elementos, luego tome dos listas, primero hechas de elementos de UNA1 , segundo hecho de elementos de UNA2 , aplique su función a ambas listas (obtener una lista de si1 en el primer caso y una lista de si2 en el segundo), y los resultados estarán relacionados, si las entradas estaban relacionadas.
foo : 'a list -> 'b list
, obtendrá que puede elegir dos tiposAhora usamos esto para probar que para cualquiera de los dos tipos
A
yB
la funciónfoo
devuelve una lista vacía para cualquier entradaas : A list
.A
y dejaØ
,B
yas
está relacionado con sí mismo (como elegimos la relación de identidadA
), por lo tantofoo as : Ø list
está relacionado confoo as : B list
.Ø
tipo.Por lo tanto, para cualquiera
A
,B
yas : A list
tenemos quefoo as : B list
tiene que ser una lista vacía.fuente