Yo juro que solía ser una camiseta a la venta con las palabras inmortales:
Que parte de
es lo que no entiende?
En mi caso, la respuesta sería ... ¡todo!
En particular, a menudo veo notación como esta en los documentos de Haskell, pero no tengo idea de lo que significa. No tengo idea de qué rama de las matemáticas se supone que es.
Por supuesto, reconozco las letras del alfabeto griego y símbolos como "∉" (que generalmente significa que algo no es un elemento de un conjunto).
Por otro lado, nunca había visto "⊢" antes ( Wikipedia afirma que podría significar "partición" ). Tampoco estoy familiarizado con el uso del vinculum aquí. (Por lo general, denota una fracción, pero ese no parece ser el caso aquí).
Si alguien al menos pudiera decirme por dónde empezar a buscar para comprender lo que significa este mar de símbolos, sería útil.
fuente
Respuestas:
:
significa tiene tipo∈
los medios están adentro . (Del mismo modo∉
significa "no está en").Γ
se usa generalmente para referirse a un entorno o contexto; en este caso, puede considerarse como un conjunto de anotaciones de tipo, que combina un identificador con su tipo. Porx : σ ∈ Γ
lo tanto, significa que el entornoΓ
incluye el hecho de quex
tiene tipoσ
.⊢
se puede leer como prueba o determina.Γ ⊢ x : σ
significa que el entornoΓ
determina quex
tiene tipoσ
.,
es una forma de incluir suposiciones adicionales específicas en un entornoΓ
.Por lo tanto,
Γ, x : τ ⊢ e : τ'
significa que el entornoΓ
, con el supuesto adicional de anulación quex
tiene tipoτ
, prueba quee
tiene tipoτ'
.Según lo solicitado: precedencia del operador, de mayor a menor:
λ x . e
,∀ α . σ
yτ → τ'
,let x = e0 in e1
y un espacio en blanco para la aplicación de la función.:
∈
y∉
,
(asociativo a la izquierda)⊢
fuente
:
y∈
son muy similares, ya que significan que una cosa está contenida en otra cosa: un conjunto contiene elementos y un tipo contiene valores, en cierto sentido. La diferencia crucial es quex ∈ S
significa que un conjuntoS
contiene literalmente un elementox
, mientrasΓ ⊢ x : T
que significa quex
se puede deducir que habita el tipoT
en contextoΓ
. Considerando esto, la regla Var dice: "Si x está literalmente contenida en el contexto, puede deducirse (trivialmente) de ella".(Γ,(x:τ))⊢(x:σ)
, ver overleaf.com/read/ddmnkzjtnqbd#/61990222Esta sintaxis, si bien puede parecer complicada, en realidad es bastante simple. La idea básica proviene de la lógica formal: toda la expresión es una implicación, siendo la mitad superior los supuestos y la mitad inferior el resultado. Es decir, si sabe que las expresiones superiores son verdaderas, puede concluir que las expresiones inferiores también son verdaderas.
Los símbolos
Otra cosa a tener en cuenta es que algunas letras tienen significados tradicionales; particularmente, Γ representa el "contexto" en el que se encuentra, es decir, cuáles son los tipos de otras cosas que ha visto. Entonces algo como
Γ ⊢ ...
significa "la expresión...
cuando conoces los tipos de cada expresión enΓ
.El
⊢
símbolo esencialmente significa que puedes probar algo. Entonces,Γ ⊢ ...
hay una declaración que dice "Puedo probar...
en un contextoΓ
. Estas declaraciones también se llaman juicios de tipo.Otra cosa a tener en cuenta: en matemáticas, al igual que ML y Scala,
x : σ
significa quex
tiene tipoσ
. Puedes leerlo como Haskell'sx :: σ
.Qué significa cada regla
Entonces, sabiendo esto, la primera expresión se vuelve fácil de entender: si sabemos que
x : σ ∈ Γ
(es decir,x
tiene algún tipoσ
en algún contextoΓ
), entonces sabemos queΓ ⊢ x : σ
(es decir, enΓ
,x
tiene tipoσ
). Entonces, realmente, esto no te dice nada súper interesante; solo te dice cómo usar tu contexto.Las otras reglas también son simples. Por ejemplo, toma
[App]
. Esta regla tiene dos condiciones:e₀
es una función de algún tipoτ
a otro tipoτ'
ye₁
es un valor de tipoτ
. Ahora se sabe qué tipo obtendrá aplicandoe₀
ae₁
! Esperemos que esto no sea una sorpresa :).La siguiente regla tiene más sintaxis nueva. En particular,
Γ, x : τ
solo significa el contexto compuesto porΓ
y el juiciox : τ
. Entonces, si sabemos que la variablex
tiene un tipo deτ
y la expresióne
tiene un tipoτ'
, también sabemos el tipo de una función que tomax
y devuelvee
. Esto simplemente nos dice qué hacer si hemos descubierto qué tipo toma una función y qué tipo devuelve, por lo que tampoco debería sorprendernos.El siguiente solo te dice cómo manejar las
let
declaraciones. Si sabe que alguna expresióne₁
tiene un tipoτ
siempre quex
tenga un tipoσ
, entonces unalet
expresión que se une localmentex
a un valor de tipoσ
hará quee₁
tenga un tipoτ
. Realmente, esto simplemente le dice que una declaración let esencialmente le permite expandir el contexto con un nuevo enlace, ¡que es exactamente lo quelet
hace!La
[Inst]
regla trata del subtipado. Dice que si tiene un valor de tipoσ'
y es un subtipo deσ
(⊑
representa una relación de orden parcial), entonces esa expresión también es de tipoσ
.La regla final trata de los tipos de generalización. Un comentario rápido: una variable libre es una variable que no es introducida por una declaración let o lambda dentro de alguna expresión; esta expresión ahora depende del valor de la variable libre de su contexto. La regla dice que si hay alguna variable
α
que no es "libre" en nada en su contexto, entonces es seguro decir que cualquier expresión cuyo tipo conozcae : σ
tendrá ese tipo para cualquier valor deα
.Cómo usar las reglas
Entonces, ahora que comprende los símbolos, ¿qué hace con estas reglas? Bueno, puedes usar estas reglas para descubrir el tipo de varios valores. Para hacer esto, mire su expresión (digamos
f x y
) y encuentre una regla que tenga una conclusión (la parte inferior) que coincida con su declaración. Llamemos a lo que está tratando de encontrar su "objetivo". En este caso, mirarías la regla que termina ene₀ e₁
. Cuando haya encontrado esto, ahora tiene que encontrar reglas que prueben todo por encima de la línea de esta regla. Estas cosas generalmente corresponden a los tipos de subexpresiones, por lo que esencialmente estás recurriendo en partes de la expresión. Simplemente haga esto hasta que termine su árbol de pruebas, que le da una prueba del tipo de su expresión.Entonces, todas estas reglas hacen especificar exactamente, y en el detalle matemáticamente pedante habitual: P, cómo averiguar los tipos de expresiones.
Ahora, esto debería sonar familiar si alguna vez ha usado Prolog: básicamente está calculando el árbol de pruebas como un intérprete Prolog humano. ¡Hay una razón por la que Prolog se llama "programación lógica"! Esto también es importante, ya que la primera forma en que me presentaron el algoritmo de inferencia HM fue implementarlo en Prolog. En realidad, esto es sorprendentemente simple y deja en claro lo que está sucediendo. Ciertamente deberías probarlo.
Nota: Probablemente cometí algunos errores en esta explicación y me encantaría que alguien los señalara. De hecho, estaré cubriendo esto en clase en un par de semanas, así que tendré más confianza entonces: P.
fuente
Γ = {x : τ}
)λy.x : σ → τ
a∀ σ. σ → τ
, pero no para∀ τ. σ → τ
, porqueτ
es variable libre enΓ
. El artículo de Wikipedia sobre HM lo explica muy bien.[Inst]
es un poco inexacta. Esto es solo lo que entiendo hasta ahora, pero las sigmas en las reglas[Inst]
y[Gen]
no se refieren a tipos, sino a esquemas de tipos . Por lo tanto, el⊑
operador es un pedido parcial no relacionado con el subtipado, tal como lo conocemos por los lenguajes OO. Está relacionado con valores polimórficos comoid = λx. x
. La sintaxis completa para tal función seríaid = ∀x. λx. x
. Ahora, obviamente, podemos tener unid2 = ∀xy. λx. x
, dondey
no se usa. Entoncesid2 ⊑ id
, que es lo que[Inst]
dice la regla.Consulte " Fundamentos prácticos de los lenguajes de programación ", capítulos 2 y 3, sobre el estilo de la lógica a través de juicios y derivaciones. Todo el libro ya está disponible en Amazon.
Capitulo 2
Definiciones Inductivas
Las definiciones inductivas son una herramienta indispensable en el estudio de lenguajes de programación. En este capítulo desarrollaremos el marco básico de definiciones inductivas y daremos algunos ejemplos de su uso. Una definición inductiva consiste en un conjunto de reglas para derivar juicios , o aserciones , de una variedad de formas. Los juicios son declaraciones sobre uno o más objetos sintácticos de un tipo específico. Las reglas especifican las condiciones necesarias y suficientes para la validez de un juicio y, por lo tanto, determinan completamente su significado.
2.1 Juicios
Comenzamos con la noción de un juicio o afirmación sobre un objeto sintáctico. Haremos uso de muchas formas de juicio, incluidos ejemplos como estos:
Un juicio establece que uno o más objetos sintácticos tienen una propiedad o se relacionan entre sí. La propiedad o relación en sí se llama forma de juicio , y el juicio de que un objeto u objetos tienen esa propiedad o se mantienen en esa relación se dice que es una instancia de esa forma de juicio. Una forma de juicio también se llama predicado , y los objetos que constituyen una instancia son sus sujetos . Escribimos una J para el juicio afirmando que J tiene de a . Cuando no es importante enfatizar el tema del juicio (el texto se corta aquí)
fuente
¿Cómo entiendo las reglas de Hindley-Milner?
Hindley-Milner es un conjunto de reglas en forma de cálculo secuencial (no deducción natural) que demuestra que podemos deducir el tipo (más general) de un programa a partir de la construcción del programa sin declaraciones de tipo explícitas.
Los símbolos y la notación.
Primero, expliquemos los símbolos y analicemos la precedencia del operador
𝜆𝑥.𝑒 significa que 𝜆 (lambda) es una función anónima que toma un argumento, 𝑥 , y devuelve una expresión, 𝑒 .
dejar 𝑥 = 𝑒₀ en 𝑒₁ significa en expresión, 𝑒₁ , sustituir 𝑒₀ donde aparezca 𝑥 .
⊑ significa que el elemento anterior es un subtipo (informalmente - subclase) del último elemento.
Todo lo que está por encima de la línea es la premisa, todo lo que está debajo es la conclusión ( Per Martin-Löf )
Precedencia, por ejemplo
Tomé algunos de los ejemplos más complejos de las reglas e inserté paréntesis redundantes que muestran precedencia:
𝚪 ⊦ 𝑥 : 𝜎 podría escribirse 𝚪 ⊦ ( 𝑥 : 𝜎 )
𝚪 ⊦ let 𝑥 = 𝑒₀ en 𝑒₁ : 𝜏 es equivalente 𝚪 ⊦ (( let ( 𝑥 = 𝑒₀ ) en 𝑒₁ ): 𝜏 )
𝚪 ⊦ 𝜆𝑥.𝑒 : 𝜏 → 𝜏 ' es equivalentemente 𝚪 ⊦ (( 𝜆𝑥.𝑒 ): ( 𝜏 → 𝜏' ))
Luego, los espacios grandes que separan las afirmaciones de afirmación y otras condiciones previas indican un conjunto de tales condiciones previas, y finalmente la línea horizontal que separa la premisa de la conclusión trae el final del orden de precedencia.
Las normas
Lo que sigue aquí son interpretaciones en inglés de las reglas, cada una seguida de una nueva declaración y una explicación.
Variable
Dicho de otra manera, en 𝚪, sabemos que 𝑥 es de tipo 𝜎 porque 𝑥 es de tipo 𝜎 en 𝚪.
Esto es básicamente una tautología. Un nombre identificador es una variable o una función.
Aplicación de funciones
Para reformular la regla, sabemos que la aplicación de función devuelve el tipo 𝜏 'porque la función tiene el tipo 𝜏 → 𝜏' y obtiene un argumento de tipo 𝜏.
Esto significa que si sabemos que una función devuelve un tipo y la aplicamos a un argumento, el resultado será una instancia del tipo que sabemos que devuelve.
Abstracción de funciones
Nuevamente, cuando vemos una función que toma 𝑥 y devuelve una expresión 𝑒, sabemos que es de tipo 𝜏 → 𝜏 'porque 𝑥 (a 𝜏) afirma que 𝑒 es a 𝜏'.
Si sabemos que 𝑥 es de tipo 𝜏 y, por lo tanto, una expresión 𝑒 es de tipo 𝜏 ', entonces una función de 𝑥 que devuelve la expresión 𝑒 es de tipo 𝜏 → 𝜏'.
Dejar declaración variable
En términos generales, 𝑥 está vinculado a 𝑒₀ en 𝑒₁ (a 𝜏) porque 𝑒₀ es un 𝜎, y 𝑥 es un 𝜎 que afirma que 𝑒₁ es un 𝜏.
Esto significa que si tenemos una expresión 𝑒₀ que es un 𝜎 (que es una variable o una función), y algún nombre, 𝑥, también un 𝜎, y una expresión 𝑒₁ de tipo 𝜏, entonces podemos sustituir 𝑒₀ por 𝑥 donde sea que aparezca dentro de 𝑒₁.
Instanciación
Una expresión, 𝑒 es de tipo padre 𝜎 porque la expresión 𝑒 es subtipo 𝜎 ', y 𝜎 es el tipo padre de 𝜎'.
Si una instancia es de un tipo que es un subtipo de otro tipo, entonces también es una instancia de ese supertipo, el tipo más general.
Generalización
Entonces, en general, 𝑒 se escribe 𝜎 para todas las variables de argumento (𝛼) que devuelven 𝜎, porque sabemos que 𝑒 es un a y 𝛼 no es una variable libre.
Esto significa que podemos generalizar un programa para aceptar todos los tipos de argumentos que aún no están vinculados en el ámbito que lo contiene (variables que no son no locales). Estas variables ligadas son sustituibles.
Poniendolo todo junto
Dados ciertos supuestos (como no hay variables libres / indefinidas, un entorno conocido), conocemos los tipos de:
Conclusión
Estas reglas combinadas nos permiten probar el tipo más general de un programa afirmado, sin requerir anotaciones de tipo.
fuente
La notación proviene de la deducción natural .
El símbolo ⊢ se llama torniquete .
Las 6 reglas son muy fáciles.
Var
la regla es bastante trivial: dice que si el tipo de identificador ya está presente en su entorno de tipos, para inferir el tipo, simplemente lo toma del entorno tal como está.App
La regla dice que si tiene dos identificadorese0
ye1
puede inferir sus tipos, puede inferir el tipo de aplicacióne0 e1
. La regla se lee así si lo sabee0 :: t0 -> t1
ye1 :: t0
(¡el mismo t0!), Entonces la aplicación está bien escrita y el tipo est1
.Abs
yLet
son reglas para inferir tipos para lambda-abstraction y let-in.Inst
La regla dice que puede sustituir un tipo con uno menos general.fuente
Hay dos formas de pensar en e: σ. Uno es "la expresión e tiene tipo σ", otro es "el par ordenado de la expresión e y el tipo σ".
Ver Γ como el conocimiento sobre los tipos de expresiones, implementado como un conjunto de pares de expresión y tipo, e: σ.
El torniquete ⊢ significa que a partir del conocimiento de la izquierda, podemos deducir lo que está a la derecha.
La primera regla [Var] puede leerse así:
si nuestro conocimiento Γ contiene el par e: σ, entonces podemos deducir de Γ que e tiene el tipo σ.
La segunda regla [App] se puede leer:
si desde Γ podemos deducir que e_0 tiene el tipo τ → τ ', y desde Γ podemos deducir que e_1 tiene el tipo τ, entonces desde Γ podemos deducir que e_0 e_1 tiene el escriba τ '.
Es común escribir Γ, e: σ en lugar de Γ ∪ {e: σ}.
La tercera regla [Abs] puede leerse así:
si desde Γ extendido con x: τ podemos deducir que e tiene el tipo τ ', entonces desde Γ podemos deducir que λx.e tiene el tipo τ → τ'.
La cuarta regla [Let] se deja como ejercicio. :-)
La quinta regla [Inst] se puede leer:
si desde Γ podemos deducir que e tiene el tipo σ ', y σ' es un subtipo de σ, entonces desde Γ podemos deducir que e tiene el tipo σ.
La sexta y última regla [Gen] puede leerse:
si desde Γ podemos deducir que e tiene tipo σ, y α no es una variable de tipo libre en ninguno de los tipos en Γ, entonces desde Γ podemos deducir que e tiene tipo ∀α σ.
fuente