Eu juro que costumava haver uma T-shirt para a venda caracteriza as palavras imortais:
Que parte de
você não entende?
No meu caso, a resposta seria ... tudo!
Em particular, muitas vezes vejo notações como essa nos artigos de Haskell, mas não tenho idéia do que isso significa. Não tenho idéia de que ramo da matemática deveria ser.
Eu reconheço as letras do alfabeto grego, é claro, e símbolos como "∉" (o que geralmente significa que algo não é um elemento de um conjunto).
Por outro lado, nunca vi "⊢" antes (a Wikipedia afirma que pode significar "partição" ). Também não estou familiarizado com o uso do vinculo aqui. (Geralmente, denota uma fração, mas não parece ser o caso aqui.)
Se alguém pudesse pelo menos me dizer por onde começar a procurar entender o que significa esse mar de símbolos, isso seria útil.
fonte
Respostas:
:
meios tem tipo∈
meio é em . (Da mesma forma,∉
significa "não está dentro".)Γ
é geralmente usado para se referir a um ambiente ou contexto; nesse caso, pode ser considerado como um conjunto de anotações de tipo, emparelhando um identificador com seu tipo. Portanto,x : σ ∈ Γ
significa que o ambienteΓ
inclui o fato de quex
tem tipoσ
.⊢
pode ser lido como prova ou determina.Γ ⊢ x : σ
significa que o ambienteΓ
determina quex
tem tipoσ
.,
é uma maneira de incluir suposições adicionais específicas em um ambienteΓ
.Portanto,
Γ, x : τ ⊢ e : τ'
significa que o ambienteΓ
, com a suposição adicional de substituição quex
possui tipoτ
, prova quee
possui tipoτ'
.Conforme solicitado: precedência do operador, do mais alto para o mais baixo:
λ x . e
,∀ α . σ
, eτ → τ'
,let x = e0 in e1
, e espaços em branco para aplicação de função.:
∈
e∉
,
(associativo à esquerda)⊢
fonte
:
e∈
são muito semelhantes, pois significam que uma coisa está contida em outra - um conjunto contém elementos e um tipo contém valores, em certo sentido. A diferença crucial é que issox ∈ S
significa que um conjuntoS
contém literalmente um elementox
, enquantoΓ ⊢ x : T
que issox
pode ser deduzido para habitar o tipoT
no contextoΓ
. Considerando isso, a regra Var diz: »Se x está literalmente contido no contexto, pode (trivialmente) ser deduzido dele«.(Γ,(x:τ))⊢(x:σ)
, ver overleaf.com/read/ddmnkzjtnqbd#/61990222Essa sintaxe, embora possa parecer complicada, é realmente bastante simples. A idéia básica vem da lógica formal: toda a expressão é uma implicação, com a metade superior sendo as suposições e a metade inferior sendo o resultado. Ou seja, se você souber que as expressões principais são verdadeiras, pode concluir que as expressões inferiores também são verdadeiras.
Símbolos
Outra coisa a ter em mente é que algumas letras têm significados tradicionais; particularmente, Γ representa o "contexto" em que você está - ou seja, quais são os tipos de outras coisas que você já viu. Então, algo como
Γ ⊢ ...
"significa a expressão...
quando você conhece os tipos de cada expressãoΓ
.O
⊢
símbolo significa essencialmente que você pode provar algo. O mesmoΓ ⊢ ...
ocorre com uma declaração dizendo "Eu posso provar...
em um contextoΓ
. Essas declarações também são chamadas de julgamentos de tipo.Outra coisa a ter em mente: em matemática, assim como ML e Scala,
x : σ
significa quex
tem tipoσ
. Você pode lê-lo como o de Haskellx :: σ
.O que cada regra significa
Portanto, sabendo disso, a primeira expressão se torna fácil de entender: se sabemos que
x : σ ∈ Γ
(isto é,x
tem algum tipoσ
em algum contextoΓ
), então sabemos queΓ ⊢ x : σ
(isto é, emΓ
,x
tem tipoσ
). Realmente, isso não está dizendo nada super interessante; apenas mostra como usar seu contexto.As outras regras também são simples. Por exemplo, pegue
[App]
. Essa regra possui duas condições:e₀
é uma função de algum tipoτ
para outro tipoτ'
ee₁
é um valor do tipoτ
. Agora você sabe que tipo obterá aplicando-see₀
ae₁
! Espero que isso não seja uma surpresa :).A próxima regra tem mais uma nova sintaxe. Particularmente,
Γ, x : τ
significa apenas o contexto compostoΓ
e o julgamentox : τ
. Portanto, se sabemos que a variávelx
tem um tipo deτ
e a expressãoe
tem um tipoτ'
, também sabemos o tipo de uma função que recebex
e retornae
. Isso nos diz o que fazer se descobrimos que tipo de função e qual tipo ela retorna, portanto também não deve surpreender.O próximo diz apenas como lidar com
let
instruções. Se você sabe que alguma expressãoe₁
tem um tipoτ
, contanto quex
tenha um tipoσ
, umalet
expressão que se liga localmentex
a um valor de tipoσ
fará com quee₁
tenha um tipoτ
. Realmente, isso apenas diz que uma instrução let permite essencialmente expandir o contexto com uma nova ligação - que é exatamente o quelet
faz!A
[Inst]
regra lida com a sub-digitação. Ele diz que, se você tem um valor de tipoσ'
e é um subtipo deσ
(⊑
representa uma relação de ordenação parcial), essa expressão também é do tipoσ
.A regra final lida com tipos de generalização. Um rápido aparte: uma variável livre é uma variável que não é introduzida por uma instrução let ou lambda dentro de alguma expressão; essa expressão agora depende do valor da variável livre em seu contexto. A regra está dizendo que, se houver alguma variável
α
que não seja "livre" em nada no seu contexto, é seguro dizer que qualquer expressão cujo tipo você conhecee : σ
terá esse tipo para qualquer valor deα
.Como usar as regras
Então, agora que você entende os símbolos, o que você faz com essas regras? Bem, você pode usar essas regras para descobrir o tipo de vários valores. Para fazer isso, observe sua expressão (digamos
f x y
) e encontre uma regra que tenha uma conclusão (a parte inferior) que corresponda à sua afirmação. Vamos chamar o que você está tentando encontrar seu "objetivo". Neste caso, você iria olhar para a regra que termina eme₀ e₁
. Quando você encontrar isso, agora precisará encontrar regras que comprovem tudo acima da linha desta regra. Geralmente, essas coisas correspondem aos tipos de sub-expressões; portanto, você está essencialmente recorrendo a partes da expressão. Você apenas faz isso até terminar sua árvore de provas, o que fornece uma prova do tipo de sua expressão.Portanto, todas essas regras fazem é especificar exatamente - e nos detalhes matematicamente usuais habituais: P - como descobrir os tipos de expressões.
Agora, isso deve parecer familiar se você já usou o Prolog - você está essencialmente computando a árvore de provas como um intérprete humano do Prolog. Há uma razão para o Prolog ser chamado de "programação lógica"! Isso também é importante, pois a primeira maneira em que fui apresentado ao algoritmo de inferência HM foi implementando-o no Prolog. Isso é surpreendentemente simples e deixa claro o que está acontecendo. Você certamente deveria tentar.
Nota: Provavelmente cometi alguns erros nesta explicação e adoraria que alguém as apontasse. Eu estarei cobrindo isso na sala de aula em algumas semanas, então ficarei mais confiante: P.
fonte
Γ = {x : τ}
)λy.x : σ → τ
para∀ σ. σ → τ
, mas não para∀ τ. σ → τ
, porqueτ
é variável livre emΓ
. O artigo da Wikipedia sobre HM explica muito bem.[Inst]
seja um pouco imprecisa. Este é apenas o meu entendimento até agora, mas os sigmas nas regras[Inst]
e[Gen]
não se referem a tipos, mas a esquemas de tipos . Portanto, o⊑
operador é um pedido parcial, não relacionado à sub-digitação, como a conhecemos nos idiomas OO. Está relacionado a valores polimórficos comoid = λx. x
. A sintaxe completa para essa função seriaid = ∀x. λx. x
. Agora, obviamente, podemos ter umid2 = ∀xy. λx. x
, ondey
não é usado. Entãoid2 ⊑ id
, é o que[Inst]
diz a regra.Veja " Fundamentos práticos de linguagens de programação " . , Capítulos 2 e 3, sobre o estilo da lógica por meio de julgamentos e derivações. O livro inteiro já está disponível na Amazon.
Capítulo 2
Definições indutivas
Definições indutivas são uma ferramenta indispensável no estudo de linguagens de programação. Neste capítulo, desenvolveremos a estrutura básica das definições indutivas e daremos alguns exemplos de seu uso. Uma definição indutiva consiste em um conjunto de regras para derivar julgamentos , ou asserções , de várias formas. Julgamentos são declarações sobre um ou mais objetos sintáticos de um tipo especificado. As regras especificam condições necessárias e suficientes para a validade de um julgamento e, portanto, determinam completamente seu significado.
2.1 Julgamentos
Começamos com a noção de julgamento ou afirmação sobre um objeto sintático. Faremos uso de muitas formas de julgamento, incluindo exemplos como estes:
Um julgamento afirma que um ou mais objetos sintáticos têm uma propriedade ou mantêm alguma relação um com o outro. A propriedade ou relação em si é chamada de forma de julgamento , e o julgamento de que um objeto ou objetos tem essa propriedade ou permanece nessa relação é considerado uma instância dessa forma de julgamento. Um formulário de julgamento também é chamado de predicado , e os objetos que constituem uma instância são seus assuntos . Escrevemos um J para o julgamento afirmando que J detém de a . Quando não é importante enfatizar o assunto do julgamento, (o texto é cortado aqui)
fonte
Como entendo as regras de Hindley-Milner?
Hindley-Milner é um conjunto de regras na forma de cálculo sequencial (não dedução natural) que demonstra que podemos deduzir o tipo (mais geral) de um programa da construção do programa sem declarações explícitas de tipo.
Os símbolos e notação
Primeiro, vamos explicar os símbolos e discutir a precedência do operador
𝑒.𝑒 significa 𝜆 (lambda) é uma função anônima que recebe um argumento 𝑥 e retorna uma expressão, 𝑒 .
deixe 𝑥 = 𝑒₀ in 𝑒₁ significa na expressão, 𝑒₁ , substitua 𝑒₀ onde quer que 𝑥 apareça.
⊑ significa que o elemento anterior é um subtipo (informalmente - subclasse) do último elemento.
Tudo acima da linha é a premissa, tudo abaixo é a conclusão ( Per Martin-Löf )
Precedência, por exemplo
Eu peguei alguns dos exemplos mais complexos das regras e inseri parênteses redundantes que mostram precedência:
𝚪 ⊦ 𝑥 : 𝜎 pode ser escrito 𝚪 ⊦ ( 𝑥 : 𝜎 )
⊦ ⊦ let 𝑥 = 𝑒₀ em 𝑒₁ : 𝜏 é equivalente 𝚪 ⊦ (( let ( 𝑥 = 𝑒₀ ) em 𝑒₁ ): 𝜏 )
𝚪 ⊦ 𝜆𝑥.𝑒 : 𝜏 → 𝜏 ' é equivalente 𝚪 ⊦ (( 𝜆𝑥.𝑒 ): ( 𝜏 → 𝜏' ))
Então, grandes espaços que separam declarações de asserção e outras pré-condições indicam um conjunto de tais pré-condições e, finalmente, a linha horizontal que separa a premissa da conclusão traz o fim da ordem de precedência.
As regras
O que se segue aqui são interpretações em inglês das regras, cada uma seguida de uma nova correção e uma explicação.
Variável
Em outras palavras, em in, sabemos que 𝑥 é do tipo 𝜎 porque 𝑥 é do tipo 𝜎 em 𝚪.
Isso é basicamente uma tautologia. Um nome de identificador é uma variável ou uma função.
Função Aplicação
Para reafirmar a regra, sabemos que o aplicativo de função retorna o tipo 𝜏 'porque a função tem o tipo 𝜏 → 𝜏' e obtém um argumento do tipo 𝜏.
Isso significa que, se soubermos que uma função retorna um tipo e a aplicamos a um argumento, o resultado será uma instância do tipo que sabemos que ela retorna.
Função Abstração
Novamente, quando vemos uma função que pega returns e retorna uma expressão 𝑒, sabemos que ela é do tipo 𝜏 → 𝜏 'porque 𝑥 (a 𝜏) afirma que 𝑒 é um 𝜏'.
Se sabemos que 𝑥 é do tipo 𝜏 e, portanto, uma expressão 𝑒 é do tipo 𝜏 ', então uma função de 𝑥 expressão retornada 𝑒 é do tipo 𝜏 → 𝜏'.
Vamos declaração variável
Vagamente, 𝑥 está vinculado a 𝑒₀ em 𝑒₁ (a 𝜏) porque 𝑒₀ é um and e 𝑥 é um 𝜎 que afirma que 𝑒₁ é um 𝜏.
Isso significa que, se tivermos uma expressão 𝑒₀ que é 𝜎 (sendo uma variável ou uma função) e algum nome, 𝑥, também a, e uma expressão 𝑒₁ do tipo 𝜏, poderemos substituir 𝑒₀ por onde quer que ela apareça de 𝑒₁.
Instanciação
Uma expressão, 𝑒 é do tipo pai 𝜎 porque a expressão 𝑒 é o subtipo 𝜎 'e 𝜎 é o tipo pai de 𝜎'.
Se uma instância é de um tipo que é um subtipo de outro tipo, também é uma instância desse supertipo - o tipo mais geral.
Generalização
Portanto, em geral, 𝑒 é digitado 𝜎 para todas as variáveis de argumento (𝛼) retornando 𝜎, porque sabemos que 𝑒 é um 𝜎 e 𝛼 não é uma variável livre.
Isso significa que podemos generalizar um programa para aceitar todos os tipos de argumentos ainda não vinculados no escopo que contém (variáveis que não são locais). Essas variáveis associadas são substituíveis.
Juntando tudo
Dadas certas suposições (como nenhuma variável livre / indefinida, um ambiente conhecido), conhecemos os tipos de:
Conclusão
Essas regras combinadas nos permitem provar o tipo mais geral de um programa declarado, sem a necessidade de anotações de tipo.
fonte
A notação vem da dedução natural .
⊢ é chamado de catraca .
As 6 regras são muito fáceis.
Var
regra é regra bastante trivial - diz que, se o tipo de identificador já está presente no seu ambiente de tipos, para inferir o tipo, você apenas o tira do ambiente como está.App
A regra diz que, se você tiver dois identificadorese0
ee1
puder inferir seus tipos, poderá inferir o tipo de aplicativoe0 e1
. A regra é lida assim se você souber dissoe0 :: t0 -> t1
ee1 :: t0
(o mesmo t0!), O aplicativo será bem digitado e o tipo serát1
.Abs
eLet
são regras para inferir tipos de abstração lambda e entrada.Inst
A regra diz que você pode substituir um tipo por um menos geral.fonte
Há duas maneiras de pensar em e: σ. Um é "a expressão e tem o tipo σ", o outro é "o par ordenado da expressão e e o tipo σ".
Veja Γ como o conhecimento sobre os tipos de expressões, implementado como um conjunto de pares de expressão e tipo, e: σ.
A catraca ⊢ significa que, pelo conhecimento à esquerda, podemos deduzir o que está à direita.
A primeira regra [Var] pode ser lida assim:
Se nosso conhecimento Γ contiver o par e: σ, podemos deduzir de Γ que e tem o tipo σ.
A segunda regra [App] pode ser lida:
se nós de Γ podemos deduzir que e_0 tem o tipo τ → τ ', e nós de Γ podemos deduzir que e_1 tem o tipo τ, então nós de Γ podemos deduzir que e_0 e_1 tem o tipo τ '.
É comum escrever Γ, e: σ em vez de Γ e {e: σ}.
A terceira regra [Abs] pode, assim, ser lida:
se de Γ estendido com x: τ podemos deduzir que e tem o tipo τ ', então de Γ podemos deduzir que λx.e tem o tipo τ → τ'.
A quarta regra [Let] é deixada como um exercício. :-)
A quinta regra [Inst] pode ser lida:
se nós de Γ podemos deduzir que e tem o tipo σ 'e σ' é um subtipo de σ, então nós de Γ podemos deduzir que e tem o tipo σ.
A sexta e a última regra [Gen] podem ser lidas:
se nós de Γ podemos deduzir que e tem o tipo σ, e α não é uma variável de tipo livre em nenhum dos tipos em Γ, então nós de Γ podemos deduzir que e tem o tipo ∀α σ.
fonte