Qual é a base matemática para os valores de primeira / segunda / terceira classe em linguagens de programação?

19

Adicionado

Acabei de encontrar duas perguntas relacionadas

/math//q/1759680/1281

/programming//a/2582804/156458


Em linguagens de programação, da Programming Language Pragmatics de Michael Scott

Em geral, é dito que um valor em uma linguagem de programação tem status de primeira classe se puder ser passado como parâmetro, retornado de uma sub-rotina ou atribuído a uma variável. Tipos simples, como números inteiros e caracteres, são valores de primeira classe na maioria das linguagens de programação. Por outro lado, um valor de "segunda classe" pode ser passado como parâmetro, mas não retornado de uma sub-rotina ou atribuído a uma variável, e um valor de "terceira classe" nem mesmo pode ser passado como parâmetro.

Os rótulos são valores de terceira classe na maioria das linguagens de programação, mas valores de segunda classe em Algol. As sub-rotinas exibem a maior variação. Eles são valores de primeira classe em todas as linguagens de programação funcionais e na maioria das linguagens de script. Eles também são valores de primeira classe em C # e, com algumas restrições, em várias outras linguagens imperativas, incluindo Fortran, Modula-2 e -3, Ada 95, C e C ++. 11 São valores de segunda classe na maioria das outras línguas imperativas e valores de terceira classe em Ada 83.

  1. Qual é a base matemática para os valores de primeira / segunda / terceira classe em linguagens de programação?

    A terminologia me lembra a lógica de primeira / segunda ordem, mas elas estão relacionadas?

  2. Parece-me que a diferença entre eles é que caso específico um valor pode ser usado

    • passado como parâmetro,
    • retornado de uma sub-rotina, ou
    • atribuído a uma variável.

    Por que os casos específicos são importantes, enquanto outros não são mencionados?

Obrigado.

Tim
fonte
23
Classificar valores em primeira / segunda classe é tão infrutífero quanto classificar linguagens em paradigmas. Tudo o que você terá são generalizações vagas que atrapalham a imagem. Esta não é a abordagem correta para entender as linguagens de programação. O que é importante é compreender sintaxe, estática e dinâmica da linguagem, mas isso é muito grande para entrar em em um comentário
gardenhead
3
Além: PL / I seria um exemplo de etiqueta como primeira classe. No PL / I, você pode declarar variáveis ​​digitadas para rotular, atribuir-lhes localizações de código e ir para elas. Você também pode passá-los como parâmetros e até criar matrizes deles.
Theraot 27/09/16
@Theraot: Talvez eu esteja namorando sozinho, mas sou o único que já teve que lidar com GOTOs atribuídos e computados no FORTRAN? E não, eu não escrevi o @ $%! código, eu estava preso re-engenharia.
Jamesqf # 27/16
@jamesqf Estou ciente de atribuir rótulos em FORTRAN e COBOL, não, não usei isso. Não sei como classificar rótulos lá. No entanto, pelo que li (tenho os manuais) PL / I vai além disso e estou convencido de que os rótulos são de primeira classe.
Theraot 27/09/16

Respostas:

45

Não existe, e é bastante arbitrário.

A única distinção útil é entre primeira classe e todas as outras. Todos os casos que estão no "outro" colchete têm seu próprio conjunto distinto de regras em cada caso e agrupá-los todos juntos não é muito útil. "Primeira classe" significa "Você não precisa procurar as regras", essencialmente, e "outro" é "Você tem que aprender as regras".

Por exemplo, em C ++, funções individuais são valores de primeira classe, desde que não tenham estado. Conjuntos de sobrecarga não são, mas lambdas. Em C #, as funções geralmente são valores de primeira classe, mas há alguns casos estranhos que surgem ao lidar com inferência de tipo que os impedem de existir em todos os casos.

DeadMG
fonte
10
+1, embora no contexto de um livro como a Pragmática da Linguagem de Programação que compara um grande número de construções em um grande número de linguagens e analise as semelhanças e diferenças e implicações em profundidade, acho que esse tipo de abreviação pode ser útil. (Só não vá ao redor esperando outras pessoas a compreender "segunda mão" e "terceira mão", como significando coisas específicas.)
ruach
(Uh, em que por "segunda mão" e "terceira mão" I naturalmente significativo "segunda classe" e "terceira classe" -P.)
ruach
3
Se você deseja negociar com funções de segunda mão, é melhor fazê-lo em um idioma em que as funções sejam cidadãos de primeira classe. ^^
5gon12eder 28/09/16
@ 5gon12eder: "funções de segunda mão" seriam aquelas que você chama de bibliotecas de terceiros, não?
Jamesqf # 28/16
11

Eu concordo com DeadMG, a distinção importante é entre primeira classe e "tudo o resto". No entanto, existe uma maneira mais familiar de classificar a diferença.

Os valores de primeira classe são dados, os outros são código. (grosso modo: tenho certeza de que há exceções. Mas essa é realmente uma boa aproximação para os idiomas do mundo real.)

Em alguns idiomas, você pode tratar o código como dados. As linguagens funcionais são famosas por isso: algumas permitem alterar o código do programa enquanto ele está sendo executado (a base da programação genética ).

Idiomas como C e C ++ permitem que você tome o endereço das funções: embora não possa modificá-las, você pode passar funções como parâmetros para outras funções. O C ++ também possui o açúcar sintático dos functores . A idéia é criar um objeto inteiro que, na superfície, pareça se comportar como uma função e possa ser transmitido como se fossem dados. Caso contrário, uma classe de valor inferior pode ser tratada como um valor de primeira classe.

Matematicamente, acho que a melhor maneira é pensar no AST de um programa . Normalmente, cada token possui um tipo específico que pode ou não ser compatível com outros tipos. Pense em valor l, valor r e a outra confusão de tipos de valor em C ++ . Em seguida, adicione as palavras-chave, símbolos que são funções etc. Alguns deles podem ser valores de primeira, segunda ou terceira classe, dependendo do idioma.

Não tenho certeza de que a "classe" de valor seja tão importante, exceto talvez em um ambiente acadêmico. No mundo real, o importante é saber como você pode transmitir o código, tratando-o como dados: functors, lambdas / closures, ponteiros de função, etc.


fonte
2
Um exemplo de um valor sem código e não de primeira classe: Lua possui uma ..."variável" especial usada para indicar parâmetros de função vararg. Você pode usá-lo em várias expressões como se fosse uma lista de valores (como print(...)ou local args = {...}), mas há algumas coisas que você não pode fazer com ela, como referir-se a ela em um fechamento ( function(...) return function() return ... end end).
Coronel Trinta e Dois
8

A Semântica Denotacional fornece uma base matemática para descrever como valores e variáveis ​​funcionam em uma linguagem de programação. Foi tão bem explicado no meu diploma de ciência da computação que obtive uma nota máxima no exame Denotational Semantics, depois esqueci a maioria e nunca tive a necessidade de usá-lo em 20 anos como programador.

Você pode optar por usar uma base matematicamente bem definida, ou pode usar terminologia informal como “status de primeira classe”. Eu teria aprendido muito mais se o curso fosse baseado na Pragmática da Linguagem de Programação de Scott, no entanto, a matemática formal é necessária para alguém que fará um doutorado em design de linguagem de programação.

Se você ler a especificação para a maioria das linguagens de programação, notará uma falta dissidente de semântica denotacional; no entanto, as linguagens mais bem projetadas tinham alguém na equipe especialista em design de linguagem de programação, portanto, entende bem a semântica denotacional.

Então Michearl Scott usa terminologia informal que tem alguma relação com a matemática formal, enquanto apresenta o assunto de uma maneira que a maioria dos programadores pode se beneficiar. Sua terminologia não é usada por outras pessoas, portanto não é útil para a comunicação, mas fornece uma boa base para as perguntas que você deve fazer ao ver uma nova linguagem de programação pela primeira vez.

Observe que Michael L. Scott é um pesquisador líder em Computer Sci; portanto, entenderá e ficará muito feliz usando a matemática formal, mas, como os melhores pesquisadores, ele é especialista em explicar a aplicação da pesquisa para o resto de nós.

Ian
fonte
Obrigado. Quais livros foram usados ​​na sua turma? Quais livros você recomenda agora? Eu sou um aprendiz de auto
Tim
@ Tim, eu fiz minha aula há mais de 20 anos! Espero que o livro de Michearl Scott seja um dos melhores e abranja mais do que você precisa, a menos que faça uma pesquisa de nível de doutorado.
Ian
3

Não, a palavra "primeiro" em "primeira classe" e "primeira ordem" significa coisas diferentes.

Mas sim, os conceitos de "primeira classe" e "primeira ordem" estão relacionados. Ambos são sobre a classificação de quais conceitos que uma linguagem pode descrever a linguagem também podem abstrair.

Um conceito é de primeira classe se os mecanismos de abstração usuais de uma linguagem puderem abstrair esse conceito.

Por exemplo, a linguagem de programação Java pode descrever números inteiros, e todos os mecanismos usuais para abstrair sobre números inteiros (aceitando-os como parâmetros de método, retornando-os como resultados de funções, armazenando-os em estruturas de dados, ...) funcionam para números inteiros.

Um conceito é de primeira ordem se não puder ser usado para abstrair sobre si mesmo.

Por exemplo, novamente em Java, podemos usar métodos para abstrair sobre certas coisas. Mas não podemos usar métodos para abstrair sobre métodos, porque um nome de método não pode ser passado como parâmetro de método. Isso é diferente no JavaScript, onde você pode usar a notação de colchete para acessar uma propriedade de um objeto pelo nome como uma sequência e pode abstrair sobre sequências.

Um conceito é de segunda ordem se puder ser usado para abstrair sobre os usos de primeira ordem em si, mas não sobre os usos de segunda ordem.

Por exemplo, em Java, você pode usar Genéricos para abstrair sobre tipos (como em class Foo<T> { public List<T> content; }). No entanto, você não pode usar genéricos para abstrair sobre genéricos (como em class Bar<T> { public T<Int> content; }). Isso é diferente em Scala.

Um conceito é de terceira ordem se puder ser usado para abstrair sobre os usos de primeira e segunda ordem de si mesmo, mas não sobre os usos de segunda ordem.

E assim por diante.

Finalmente, um conceito é de ordem superior se puder ser usado para abstrair sobre usos arbitrários de si mesmo.

Resumo: Se um recurso de abstração é de primeira classe, também é de ordem superior. E se um recurso de abstração é de primeira ordem, não pode ser de primeira classe.

Toxaris
fonte
1
Você pode fazer List<List>em Java, no entanto. Talvez você possa esclarecer o que você quer dizer com essa parte?
Polygnome
1
Bom ponto. Quero dizer: class Foo<A> { A<Int> ... }. Ou seja, pretendo usar um parâmetro de tipo genérico como uma classe genérica. Em seguida, Foo<List>instanciaria o A<Int>para List<Int>. Em Java, isso não é possível. Vou tentar editar isso na resposta mais tarde.
Toxaris 27/09/16
Na verdade, isso não é possível.
Polygnome
Agora substituí o List<List>exemplo enganoso . Obrigado por apontar o problema, @Polygnome.
Toxaris 27/09/16
@ Toxaris Eu acho que isso é apenas uma terminologia idiossincrática que você criou. Um 'conceito' não é de primeira ou segunda ordem, é um quantificador lógico.
Route de milhas
2

Qual é a base matemática para os valores de primeira / segunda / terceira classe em linguagens de programação?

Nada que eu saiba.

A terminologia me lembra a lógica de primeira / segunda ordem, mas elas estão relacionadas?

Na verdade não.

A "classe" de um elemento da linguagem de programação é apenas uma maneira abreviada de pensar sobre a pergunta que coisas o usuário da minha linguagem deseja manipular programaticamente ? Por exemplo, o C # oferece um rico conjunto de operações para manipular valores , um conjunto menos rico de maneiras de manipular tipos e nenhuma operação que manipula rótulos .

No entanto, sua intuição de que existe uma conexão aqui não é totalmente equivocada. Existe uma analogia a ser feita da lógica de primeira ordem à programação procedural e da lógica de ordem superior à programação funcional. A lógica de primeira ordem trata da manipulação lógica de valores; programação procedural trata de manipulação programática de valores. A lógica de ordem superior refere-se à manipulação lógica de declarações da lógica , a programação funcional trata da manipulação programática de funções .

Por que os casos específicos são importantes, enquanto outros não são mencionados?

Você teria que pedir ao autor uma resposta definitiva.

Eu não ficaria muito preocupado com essa noção de "classe". Não é uma coisa formalmente definida. É um atalho que os designers de linguagem usam para falar sobre que tipos de coisas podem ser manipuladas programaticamente.

Eric Lippert
fonte
2

"Valor de primeira classe", neste contexto, é uma terminologia padrão na teoria da linguagem de programação. Um valor de primeira classe é algo que pode ser manipulado como valores normais na linguagem, algo que pode ser calculado em tempo de execução. Obviamente, essa é uma definição tautológica até que você defina uma semântica para a linguagem e, em seguida, um valor seja o que a semântica definir como valor. O objetivo do conceito é identificar o que pode ser manipulado diretamente, em vez de apenas acessado indiretamente.

Por exemplo, em quase todas as linguagens de programação, números inteiros de máquinas de tamanho limitado (por exemplo, números inteiros de 8 bits, números inteiros de 32 bits ou números inteiros de 64 bits, etc.) são valores de primeira classe. Você pode armazená-los em variáveis, passá-los e retorná-los às funções, etc. Na maioria dos idiomas, mas não nos idiomas de baixo nível, como assembly e C, as strings são valores de primeira classe - mas em C, eles não são, você só obter ponteiros para seqüências de caracteres. Em C, strings e matrizes não são valores de primeira classe: por exemplo, você não pode passar um array para uma função, não pode atribuir um array a uma variável de array, etc. Em C, funções não são valores de primeira classe ou: você não pode armazenar uma função em uma variável, apenas um ponteiro para uma função. Por outro lado, strings e funções são valores de primeira classe na maioria das linguagens de programação de alto nível: você pode armazená-los em uma string, etc.

Um exemplo de um conceito que não é de primeira classe em muitas linguagens de programação projetadas para serem compiladas são os tipos. Em uma linguagem como C ou Java, os tipos vivem em tempo de compilação, você não pode manipulá-los usando construções de linguagem. (Java também possui um sistema de tipo dinâmico baseado em classes; classes são valores de primeira classe por meio de reflexão.) Por outro lado, uma linguagem como Python tem uma typefunção que retorna um valor que representa o tipo de seu argumento.

A negação do "valor de primeira classe" na terminologia padrão "não é um valor de primeira classe". O termo "valor de segunda classe" não é comumente usado e "valor de terceira classe" ainda menos. Não espere vê-los fora deste livro. Não existe absolutamente nenhuma base para definir "segundo" como "pode ​​ser passado como parâmetro" e "terceiro" como "não pode ser passado como parâmetro", não há escala de coisas que possam ser numeradas de maneira significativa. Pouquíssimos idiomas fazem a diferença entre valores que podem ser passados ​​como parâmetro para funções e valores que podem ser atribuídos a variáveis, portanto, não é útil definir um nome para esse conceito.

Gilles 'SO- parar de ser mau'
fonte