Auto (linguagem de programação) - Self (programming language)

Auto
Logotipo
Paradigma orientado a objeto ( baseado em protótipo )
Projetado por David Ungar , Randall Smith
Desenvolvedor David Ungar, Randall Smith, Stanford University , Sun Microsystems
Apareceu pela primeira vez 1987 ; 34 anos atrás ( 1987 )
Versão estável
Mandarim 2017.1 / 24 de maio de 2017 ; 4 anos atras ( 24-05-2017 )
Disciplina de digitação dinâmico , forte
Licença Licença tipo BSD
Local na rede Internet www .selflanguage .org
Implementações principais
Auto
Influenciado por
Smalltalk , APL
Influenciado
NewtonScript , JavaScript , Io , Agora , Squeak , Lua , Factor , REBOL

Self é uma linguagem de programação orientada a objetos baseada no conceito de protótipos . Self começou como um dialeto de Smalltalk , sendo dinamicamente tipado e usando compilação just-in-time (JIT), bem como a abordagem baseada em protótipo para objetos: foi usado pela primeira vez como um sistema de teste experimental para design de linguagem nas décadas de 1980 e 1990 . Em 2006, Self ainda estava sendo desenvolvido como parte do projeto Klein, que era uma máquina virtual Self escrita totalmente em Self. A versão mais recente é 2017.1 lançada em maio de 2017.

Várias técnicas de compilação just-in-time foram pioneiras e aprimoradas na pesquisa do Self, pois eram necessárias para permitir que uma linguagem orientada a objetos de alto nível funcionasse a até metade da velocidade do C. Muito do desenvolvimento do Self ocorreu na Sun Microsystems , e as técnicas que eles desenvolveram mais tarde foram implantados para Java 's HotSpot máquina virtual .

Em um ponto, uma versão do Smalltalk foi implementada no Self. Por ser capaz de usar o JIT, também apresentou um desempenho extremamente bom.

História

Self foi projetado principalmente por David Ungar e Randall Smith em 1986, enquanto trabalhava no Xerox PARC . Seu objetivo era impulsionar o estado da arte na pesquisa de linguagens de programação orientadas a objetos, uma vez que Smalltalk-80 foi lançado pelos laboratórios e começou a ser levado a sério pela indústria. Eles se mudaram para a Universidade de Stanford e continuaram a trabalhar na linguagem, construindo o primeiro compilador Self funcional em 1987. Nesse ponto, o foco mudou para tentar trazer um sistema inteiro para o Self, em vez de apenas a linguagem.

O primeiro lançamento público foi em 1990 e, no ano seguinte, a equipe mudou-se para a Sun Microsystems, onde continuou a trabalhar na linguagem. Vários novos lançamentos se seguiram até cair em grande parte dormente em 1995 com a versão 4.0. A versão 4.3 foi lançada em 2006 e rodava em Mac OS X e Solaris . Uma nova versão em 2010, a versão 4.4, foi desenvolvida por um grupo composto por alguns membros da equipe original e programadores independentes e está disponível para Mac OS X e Linux , assim como todas as versões a seguir. O follow-up 4.5 foi lançado em janeiro de 2014 e, três anos depois, a versão 2017.1 foi lançada em maio de 2017.

Self também inspirou várias linguagens com base em seus conceitos. Mais notáveis, talvez, foram o NewtonScript para o Apple Newton e o JavaScript usados ​​em todos os navegadores modernos. Outros exemplos incluem Io , Lisaac e Agora . O sistema de objetos distribuídos do IBM Tivoli Framework , desenvolvido em 1990, era, no nível mais baixo, um sistema de objetos baseado em protótipo inspirado em Self.

Linguagens de programação baseadas em protótipos

As linguagens OO tradicionais baseadas em classes são baseadas em uma dualidade profundamente enraizada:

  1. As classes definem as qualidades e comportamentos básicos dos objetos.
  2. Instâncias de objeto são manifestações particulares de uma classe.

Por exemplo, suponha que os objetos da Vehicleclasse tenham um nome e a capacidade de realizar várias ações, como dirigir para o trabalho e entregar materiais de construção . Bob's caré um objeto particular (instância) da classe Vehicle, com o nome "Carro de Bob". Em teoria, pode-se enviar uma mensagem para Bob's car, dizendo-lhe para entregar materiais de construção .

Este exemplo mostra um dos problemas com essa abordagem: o carro de Bob, que por acaso é um carro esporte, não é capaz de transportar e entregar materiais de construção (em qualquer sentido significativo), mas essa é uma capacidade que Vehicleos modelos têm. Um modelo mais útil surge do uso de subclasses para criar especializações de Vehicle; por exemplo Sports Care Flatbed Truck. Apenas os objetos da classe Flatbed Truckprecisam fornecer um mecanismo para entregar materiais de construção ; carros esportivos, que não são adequados para esse tipo de trabalho, precisam apenas dirigir rápido . No entanto, esse modelo mais profundo requer mais insights durante o projeto, insights que podem vir à tona à medida que surgem problemas.

Esse problema é um dos fatores motivacionais por trás dos protótipos . A menos que se possa prever com certeza quais qualidades um conjunto de objetos e classes terá em um futuro distante, não se pode projetar uma hierarquia de classes de maneira adequada. Com muita freqüência, o programa eventualmente precisaria de comportamentos adicionais, e seções do sistema precisariam ser redesenhadas (ou refatoradas ) para separar os objetos de uma maneira diferente. A experiência com as primeiras linguagens OO, como Smalltalk, mostrou que esse tipo de problema surgia repetidamente. Os sistemas tenderiam a crescer até um determinado ponto e então se tornarem muito rígidos, à medida que as classes básicas abaixo do código do programador se tornassem simplesmente "erradas". Sem uma maneira de mudar facilmente a classe original, problemas sérios podem surgir.

Linguagens dinâmicas como Smalltalk permitiam esse tipo de mudança por meio de métodos bem conhecidos nas classes; mudando a classe, os objetos baseados nela mudariam seu comportamento. No entanto, essas mudanças tiveram que ser feitas com muito cuidado, pois outros objetos baseados na mesma classe podem estar esperando este comportamento "errado": "errado" geralmente depende do contexto. (Essa é uma forma do problema da classe base frágil .) Além disso, em linguagens como C ++ , em que as subclasses podem ser compiladas separadamente das superclasses, uma mudança em uma superclasse pode, na verdade, quebrar métodos de subclasse pré-compilados. (Esta é outra forma do problema da classe base frágil e também uma forma do problema da interface binária frágil .)

No Self e em outras linguagens baseadas em protótipos, a dualidade entre classes e instâncias de objetos é eliminada.

Em vez de ter uma "instância" de um objeto que se baseia em alguma "classe", em Self faz-se uma cópia de um objeto existente e o altera. Portanto, Bob's carseria criado fazendo uma cópia de um objeto "Vehicle" existente e, em seguida, adicionando o método drive fast , modelando o fato de que se trata de um Porsche 911 . Os objetos básicos usados ​​principalmente para fazer cópias são conhecidos como protótipos . Esta técnica é considerada para simplificar muito o dinamismo. Se um objeto existente (ou conjunto de objetos) provar ser um modelo inadequado, um programador pode simplesmente criar um objeto modificado com o comportamento correto e usá-lo em seu lugar. O código que usa os objetos existentes não é alterado.

Descrição

Os objetos próprios são uma coleção de "slots". Slots são métodos de acesso que retornam valores, e colocar dois pontos após o nome de um slot define o valor. Por exemplo, para um slot chamado "nome",

myPerson name

retorna o valor em nome, e

myPerson name:'foo'

define.

Self, como Smalltalk, usa blocos para controle de fluxo e outras funções. Métodos são objetos que contêm código além de slots (que usam para argumentos e valores temporários) e podem ser colocados em um slot Self como qualquer outro objeto: um número, por exemplo. A sintaxe permanece a mesma em ambos os casos.

Observe que não há distinção em Self entre campos e métodos: tudo é um slot. Visto que acessar slots por meio de mensagens forma a maior parte da sintaxe em Self, muitas mensagens são enviadas para "self" e o "self" pode ser deixado de fora (daí o nome).

Sintaxe básica

A sintaxe para acessar slots é semelhante à de Smalltalk. Três tipos de mensagens estão disponíveis:

unário
receiver slot_name
binário
receiver + argument
palavra-chave
receiver keyword: arg1 With: arg2

Todas as mensagens retornam resultados, portanto, o receptor (se presente) e os argumentos podem ser o resultado de outras mensagens. Seguir uma mensagem por um ponto significa que Self descartará o valor retornado. Por exemplo:

'Hello, World!' print.

Esta é a versão Self do programa hello world . A 'sintaxe indica um objeto de string literal. Outros literais incluem números, blocos e objetos gerais.

O agrupamento pode ser forçado usando parênteses. Na ausência de agrupamento explícito, as mensagens unárias são consideradas como tendo a precedência mais alta, seguidas pelas binárias (agrupamento da esquerda para a direita) e as palavras-chave tendo a mais baixa. O uso de palavras-chave para atribuição levaria a alguns parênteses extras onde as expressões também tinham mensagens de palavras-chave, portanto, para evitar que Self exija que a primeira parte de um seletor de mensagem de palavra-chave comece com uma letra minúscula e as partes subsequentes comecem com uma letra maiúscula.

valid: base bottom
          between: ligature bottom + height
          And: base top / scale factor.

pode ser analisado sem ambigüidade e significa o mesmo que:

valid: ((base bottom)
            between: ((ligature bottom) + height)
            And: ((base top) / (scale factor))).

Em Smalltalk-80, a mesma expressão seria escrita como:

valid := self base bottom
             between: self ligature bottom + self height
             and: self base top / self scale factor.

assumindo base, ligature, heighte scalenão eram variáveis de instância de selfmas eram, de facto, os métodos.

Fazendo novos objetos

Considere um exemplo um pouco mais complexo:

labelWidget copy label: 'Hello, World!'.

faz uma cópia do objeto "labelWidget" com a mensagem de cópia (sem atalho dessa vez) e envia uma mensagem para colocar "Hello, World" no slot chamado "label". Agora, para fazer algo com isso:

(desktop activeWindow) draw: (labelWidget copy label: 'Hello, World!').

Nesse caso, o (desktop activeWindow)é executado primeiro, retornando a janela ativa da lista de janelas que o objeto da área de trabalho conhece. Em seguida (leia de interno para externo, da esquerda para a direita), o código que examinamos anteriormente retorna o labelWidget. Finalmente, o widget é enviado para o slot de sorteio da janela ativa.

Delegação

Em teoria, cada objeto Self é uma entidade autônoma. Self não tem classes nem metaclasses. Mudanças em um determinado objeto não afetam nenhum outro, mas em alguns casos é desejável que afetem. Normalmente, um objeto pode entender apenas as mensagens correspondentes aos seus slots locais, mas por ter um ou mais slots indicando objetos pais , um objeto pode delegar qualquer mensagem que não entende para o objeto pai. Qualquer slot pode se tornar um ponteiro pai adicionando um asterisco como sufixo. Desta forma, Self lida com tarefas que usariam herança em linguagens baseadas em classe. A delegação também pode ser usada para implementar recursos como namespaces e escopo léxico .

Por exemplo, suponha que um objeto seja definido como "conta bancária", que é usado em um aplicativo simples de contabilidade. Normalmente, esse objeto seria criado com os métodos internos, talvez "depósito" e "retirada", e quaisquer slots de dados necessários para eles. Este é um protótipo, que só é especial na forma como é usado, uma vez que também passa a ser uma conta bancária totalmente funcional.

Características

Fazer um clone deste objeto para a "conta do Bob" criará um novo objeto que começa exatamente como o protótipo. Neste caso, copiamos os slots, incluindo os métodos e quaisquer dados. No entanto, uma solução mais comum é primeiro fazer um objeto mais simples, denominado objeto de características, que contém os itens que normalmente se associam a uma classe.

Neste exemplo, o objeto "conta bancária" não teria o método de depósito e retirada, mas teria como pai um objeto que tinha. Desta forma, muitas cópias do objeto conta bancária podem ser feitas, mas ainda podemos mudar o comportamento de todos eles, alterando os slots nesse objeto raiz.

Como isso é diferente de uma aula tradicional? Bem, considere o significado de:

myObject parent: someOtherObject.

Este trecho altera a "classe" de myObject no tempo de execução, alterando o valor associado ao slot 'pai *' (o asterisco faz parte do nome do slot, mas não das mensagens correspondentes). Ao contrário da herança ou escopo léxico, o objeto delegado pode ser modificado em tempo de execução.

Adicionando slots

Os objetos no Self podem ser modificados para incluir slots adicionais. Isso pode ser feito usando o ambiente de programação gráfico ou com o primitivo '_AddSlots:'. Uma primitiva tem a mesma sintaxe de uma mensagem de palavra-chave normal, mas seu nome começa com o caractere de sublinhado. A primitiva _AddSlots deve ser evitada porque é uma sobra das primeiras implementações. No entanto, vamos mostrar isso no exemplo abaixo porque torna o código mais curto.

Um exemplo anterior foi sobre como refatorar uma classe simples chamada Vehicle para poder diferenciar o comportamento entre carros e caminhões. Em Si mesmo, alguém faria isso com algo assim:

_AddSlots: (| vehicle <- (|parent* = traits clonable|) |).

Como o receptor do primitivo '_AddSlots:' não é indicado, ele é "self". No caso de expressões digitadas no prompt, trata-se de um objeto denominado "lobby". O argumento para '_AddSlots:' é o objeto cujos slots serão copiados para o receptor. Nesse caso, é um objeto literal com exatamente um slot. O nome do slot é 'veículo' e seu valor é outro objeto literal. A notação "<-" implica um segundo slot chamado 'veículo:' que pode ser usado para alterar o valor do primeiro slot.

O "=" indica um slot constante, então não há 'pai:' correspondente. O objeto literal que é o valor inicial de 'veículo' inclui um único slot para que ele possa entender as mensagens relacionadas à clonagem. Um objeto verdadeiramente vazio, indicado como (| |) ou mais simplesmente como (), não pode receber nenhuma mensagem.

vehicle _AddSlots: (| name <- 'automobile'|).

Aqui, o receptor é o objeto anterior, que agora incluirá 'nome' e 'nome:' slots além de 'pai *'.

_AddSlots: (| sportsCar <- vehicle copy |).
sportsCar _AddSlots: (| driveToWork = (''some code, this is a method'') |).

Embora anteriormente 'veículo' e 'carro esportivo' fossem exatamente iguais, agora o último inclui um novo slot com um método que o original não possui. Os métodos só podem ser incluídos em slots constantes.

_AddSlots: (| porsche911 <- sportsCar copy |).
porsche911 name:'Bobs Porsche'.

O novo objeto 'porsche911' começou exatamente como 'sportsCar', mas a última mensagem mudou o valor de seu slot de 'nome'. Observe que ambos ainda têm exatamente os mesmos slots, embora um deles tenha um valor diferente.

Meio Ambiente

Uma característica do Self é que ele se baseia no mesmo tipo de sistema de máquina virtual que os sistemas Smalltalk anteriores usavam. Ou seja, os programas não são entidades autônomas como em linguagens como C , mas precisam de todo o ambiente de memória para serem executados. Isso requer que os aplicativos sejam enviados em blocos de memória salvos, conhecidos como instantâneos ou imagens . Uma desvantagem dessa abordagem é que as imagens às vezes são grandes e difíceis de manejar; no entanto, depurar uma imagem geralmente é mais simples do que depurar programas tradicionais porque o estado do tempo de execução é mais fácil de inspecionar e modificar. (A diferença entre o desenvolvimento baseado na fonte e o baseado na imagem é análoga à diferença entre a programação baseada em classe e a programação orientada a objetos prototípica.)

Além disso, o ambiente é adaptado para a mudança rápida e contínua dos objetos no sistema. Refatorar um projeto de "classe" é tão simples quanto arrastar métodos dos ancestrais existentes para novos. Tarefas simples, como métodos de teste, podem ser realizadas fazendo uma cópia, arrastando o método para a cópia e, em seguida, alterando-o. Ao contrário dos sistemas tradicionais, apenas o objeto alterado possui o novo código e nada precisa ser reconstruído para testá-lo. Se o método funcionar, ele pode simplesmente ser arrastado de volta ao ancestral.

atuação

Auto VMs alcançaram desempenho de aproximadamente metade da velocidade do C otimizado em alguns benchmarks.

Isso foi alcançado por meio de técnicas de compilação just-in-time que foram pioneiras e aprimoradas na autopesquisa para fazer uma linguagem de alto nível ter um bom desempenho.

Coleta de lixo

O coletor de lixo para Self usa coleta de lixo geracional que separa objetos por idade. Usando o sistema de gerenciamento de memória para gravar gravações de página, uma barreira de gravação pode ser mantida. Essa técnica oferece excelente desempenho, embora depois de executada por algum tempo, uma coleta de lixo completa possa ocorrer, levando um tempo considerável.

Otimizações

O sistema de tempo de execução nivela seletivamente as estruturas de chamada. Isso proporciona acelerações modestas em si, mas permite extenso armazenamento em cache de informações de tipo e várias versões de código para diferentes tipos de chamador. Isso elimina a necessidade de fazer muitas pesquisas de método e permite que instruções de desvio condicional e chamadas embutidas em código sejam inseridas - geralmente proporcionando um desempenho semelhante ao do C sem perda de generalidade no nível da linguagem, mas em um sistema totalmente coletado pelo lixo.

Veja também

Referências

Leitura adicional

links externos