Compartilhar via


Este artigo foi traduzido por máquina.

O programador Polyglot

Reaping os benefícios de Cobra

Ted Neward

Conteúdo

Inícios
Confiar mas verificar
" É uma sobremesa topping; é uma andar cera. É ambos!"
Soma para cima

Na primeira edição desta coluna, discuti a importância de compreender mais de uma linguagem de programação e, em particular, mais de um tipo de programação idioma; apenas conhecer C# e / ou Visual Basic não é suficiente para destacar contra o mob mais. As ferramentas mais sua caixa de ferramentas, melhor será o carpinteiro você tornar-se. (Para cima a um ponto, mesmo assim — ' tis o ruim carpinteiro que possui cada ferramenta já feita e ainda ainda não souber como usar qualquer uma delas.)

Nesta edição, irá examinar um idioma que não muito é removido dos idiomas orientado a objeto que você está familiarizado, mas possui alguns novos recursos que não oferecerá apenas algumas novas idéias, mas possivelmente inicie algumas novas maneiras de pensar sobre a ferramenta atual definido.

Permitir que eu apresentar a você Cobra, um descendente do Python que, entre outras coisas, oferece um modelo de programação dinâmico e estaticamente digitado combinado, instalações de teste de unidade interna, recursos de script e algumas declarações de design por contrato.

Inícios

Como Lewis Carroll é famosas para dizer, vamos "começa no início." Cobra é uma linguagem .NET que pode ser obtida por meio do navegador da Web, em cobra-language.org, ou por meio de um cliente Subversion no mesmo domínio. Se a criação da fonte, usar o script install-de-workspace.bat no diretório de origem para criar o compilador Cobra e instalar os assemblies necessários no cache global de assemblies (GAC). Durante a criação de origem não é necessária — binários predefinidos também estão disponíveis — geralmente é interessante examinar a origem do compilador do Cobra, que se está escrito em Cobra (uma etapa significativa em qualquer idioma), como uma maneira de ver muitos, se não todas, dos recursos de idioma no trabalho em um programa não trivial.

Depois que o compilador é criado, a primeira etapa é para testar o idioma Cobra por meio do todos Favoritos "teste-a-compilador" (ou é "teste-a-programador"), a todos os lugares Hello World programa:

class Program
        def main is shared
                print 'Hello, world.'

Cobra oferece duas maneiras diferentes para interagir com seu código-fonte: tanto o código pode ser executado diretamente por meio de um script - como abordagem executando "cobra Hello.cobra" na linha de comando, ou o código pode ser compilado e, em seguida, executados no modo tradicional de idiomas/Visual Basic/c++ C#, por compilá-lo primeiro ("Hello.cobra –compile de cobra") e executá-lo como faria com qualquer executável .NET. Observe que o executável "cobra" é o mesmo em ambos os casos, o que criado no diretório Cobra\Source. (Observant leitores de examinar o conteúdo do diretório após "script" o arquivo .cobra irão notar que "scripts" modo do Cobra é realmente apenas uma seqüência de compilação, em seguida, executar de dois passos, deixando o hello.exe compilado" por trás após ele é concluído executar.)

Observando a fonte Cobra, ele se torna aparente que embora haja algumas semelhanças distintas com as linguagens C# e Visual Basic, há também algumas distintas diferenças. Na verdade, os leitores conhece Python (ou seu equivalente do CLR, a IronPython) serão rapidamente Observe que Cobra principalmente deriva a sintaxe Python — do Cobra inventor, Chuck Esterbrook, encontradas a sintaxe Python atraente e usado que como base da qual derivam seu idioma.

Para aqueles que nunca usou Python antes, no entanto, uma diferença imediatamente destaca: em vez de usar palavras-chave (como "Iniciar" e "End") ou símbolos (como "{" e"}") para definir os blocos de código além do código ao redor, Cobra — como Python — usa o espaço em branco significativo, ou, como prefere do Cobra inventor, recuados blocos, que significa que blocos de código são definidos separados pelo recuo léxico. Em outras palavras, final de um bloco de tomada de decisão (um bloco "se") é sinalizada não por uma chave de fechamento, mas pelo fato de que a próxima linha é um nível de recuo removido da linha anterior. Assim, na Figura 1 , Cobra sabe que a linha "impressão" final é executada, independentemente dos resultados do teste "se", porque essa linha é recuada igualmente para a declaração de "se" que tomou a decisão.

Figura 1 estrutura de recuo no código Cobra

class Program
        def main is shared
                if 1==1
                        print "Oh, goody, math works!"
                        print "I was beginning to get worried there."
                print "Math test complete"

Por causa dos problemas que surgem se você combinar as tabulações e espaços em um único arquivo para controlar o recuo, Cobra exige um/ou, mas não ambos, gerando um erro de compilador onde ele vê uma mistura dos dois.

Além disso, Cobra tem codified tendencies ritualistic o nome da clareza de código:

  • Tipos como classes, estruturas, interfaces e enumerações, deve ser capitalizados e não podem começar com um sublinhado, como os padrões estabelecidos pela Microsoft na biblioteca de classe de base: String, controle ApPDomain e assim por diante.
  • Argumentos e variáveis locais devem começar com uma letra minúscula e não podem começar com um sublinhado. Os exemplos incluem x, y, contagem e índice.
  • Em expressões, acesso a membros do objeto ou classe é sempre feito explicitamente com um ponto (Foo) ou implicitamente se o membro for iniciado com um sublinhado ("_foo").
  • Embora não aplicadas, variáveis de objeto normalmente são precedidas por um sublinhado, que também implica visibilidade protegida. Exemplos incluem _serialNum, _color e _parent.

Portanto, para qualquer determinado fragmento de código Cobra, um programador Cobra pode distinguir facilmente os elementos envolvidos na fragmento ou trecho. Por exemplo, na seguinte instrução de Cobra

    e = Element(alphabet[_random.next(alphabet.length)])

podemos dizer que

  • alfabeto e e são variáveis locais ou argumentos
  • _random é um objeto membro/variável, de visibilidade protegido (provavelmente)
  • Elemento é um tipo

Cobra, portanto, segue a premissa básica de Python: que para qualquer determinado cenário, há apenas uma maneira de fazê-lo, incluindo o código de nomeação e convenções de formatação.

Confiar mas verificar

Confiar mas verificar. Essas palavras, famously uttered por ex-americano Presidente Ronald Reagan (no contexto de negociações de redução de braços), mantenha apenas como altamente para a programação como faziam para redução de braços nuclear. Embora seja fácil de acreditar — e confiança — que os programadores de colegas de trabalho-site sempre saberia melhor do que para passar uma referência nula para um método que documenta claramente o fato de que fazer para que irá gerar uma NullReferenceException, ele é geralmente foi aprovado repetidas (especialmente durante demonstrações antes do cliente ou o chefe grande) que o curso mais seguro está tornar essas suposições explícita. Na verdade, o presidente ex-estava ativo hoje e ocupa programação, é totalmente possível que ele deve corrigir suas palavras para leitura, "relação de confiança, mas o documento, insistir e verificar."

No mundo programação, isso significa que duas coisas básicas: para insistir para programar defensiva, geralmente usando instruções assert ou métodos para garantir (no risco de uma exceção de tempo de execução) que valores passados na correspondência certos critérios; para verificar, está escrever testes de unidade com o método para garantir que a implementação do método trata o programador pathological que insiste em violando essas restrições.

Considere a tarefa (relativamente simples) de criação de uma classe que representa uma pessoa dentro do sistema. Pessoas geralmente têm um nome, um sobrenome e uma idade e podem ser casadas com outras pessoas. Em geral, ao escrever esses tipos de classes, você precisará estabelecer algumas constantes — princípios básicos que armazenará a qualquer uso da classe. Portanto, por exemplo, uma classe Person pode decidir que um nome de último não pode ser nulo, algo que é geralmente aplicada pelo desenvolvedor escrever pessoa e geralmente feita na construção de conjunto de propriedade da classe, como você vê no código C# na Figura 2 .

A Figura 2 constantes impor restrições

public class Person
{
  public Person(string fn, string ln, int a)
  {
    this.firstName = fn;
    this.lastName = ln;
    this.age = a;
  }
  public string FirstName
  {
    get { return firstName; }
    set { firstName = value; }
  }
  public string LastName
  {
    get { return lastName; }
    set { if ((value != null) || value.Length > 0) lastName = value; }
  }
  public int Age
  {
    get { return age; }
    set { age = value; }
  }
  public override string ToString()
  {
    string ret = String.Format("Person: 
  }
}

O problema com isso é que a constante seja reforçada somente quando a propriedade-setter é usada, se outro desenvolvedor vem em torno posteriormente e coloca um novo método na pessoa que referencia diretamente o armazenamento de backup (o campo encapsulado pela propriedade), a constante é quebrada silenciosamente e sutilmente, portanto, e ninguém observará-lo até que ela seja muito tarde. (Isso é o coração do debate ao redor se classes devem usar propriedades internamente ou manipular diretamente os campos.) Esta pergunta, obviamente, se aplica igualmente a propriedades e métodos. Embora não é com freqüência que uma propriedade toca campo mais de um existente, não é proibido pela linguagem, e isso acontecer periodicamente. Ainda maior preocupação, toda e qualquer constante (primeiro nome não pode ser nulo ou vazio, último nome não pode ser nulo ou vazio, idade não pode ser negativa) precisará ser aplicada, em cada método, propriedade ou construtor que potencialmente pode modificar o estado interno.

Cobra oferece um sistema de design por contrato básico, em que uma classe ou método pode declarar certos requisitos sobre a classe, e o compilador Cobra será silenciosamente adicionar instruções de verificação para cada membro (ou propriedade ou método) que poderiam potencialmente modificar dados em questão. Dessa forma, é garantido que sobrenome nunca for nulo, independentemente de como a classe é usada e sem a necessidade explicitamente código de cada método, que, possivelmente, pode alterar o conteúdo da instância (veja a Figura 3 ).

Instruções de verificação a Figura 3

class Person
    invariant
        .firstName <> ""
        .firstName.length > 0
        .lastName <> ""
        .lastName.length > 0
        .age >= 0

    var _firstName as String
    var _lastName as String
    var _age as number

    pro firstName from var
    pro lastName from var
    pro age from var

    cue init(first as String, last as String, age as number)
        ensure
            first.length > 0
            last.length > 0
            age > -1
        body
            _firstName = first
            _lastName = last
            _age = age

    def toString as String is override
        return 'Person: [.firstName] [.lastName], [.age.toString] years'

É relativamente fácil verificar exatamente quanto e com que freqüência o idioma Cobra insere essas verificações de validação, disparando até o disassembler IL (ILDasm.exe) e examinando a classe Person resultante. Fazendo revela caso a beleza da abordagem Cobra — cada método de "Modificar" é anexado com o código para verificar a constante, torná-los toda de classe. (Há uma opção de compilador para alterar como as constantes são gerados — não na todas ou apenas em métodos ou, o padrão anexado ao todas essas operações.)

No e de si mesmo, o recurso de design por contrato do Cobra é ótimo, mas é ainda não é suficiente para garantir programas livres de erros; além disso, é quase sempre necessário gravar uma série de testes de unidade com o código para garantir que ele se comporta conforme o desejado em que os dados do boas e defeituosos é passado. Novamente, considerando que a isso foi tão fundamental para o processo de desenvolvimento de software, Cobra escolher tornar esta uma construção de primeira classe dentro de idioma — "teste" construir da linguagem — como que Cobra pode executar os testes como parte do processo de compilação. Portanto, por exemplo, um conjunto simples de testes de unidade pode parecer Figura 4 para a classe Person.

Figura 4 testes de unidade para a pessoa

class Person
    invariant
        .firstName <> ""
        .firstName.length > 0
        .lastName <> ""
        .lastName.length > 0
        .age >= 0
    test
        p = Person('Neal', 'Ford', 29)
        assert p.firstName == 'Neal'
        assert p.lastName == 'Ford'
        assert p.age == 29

    var _firstName as String
    var _lastName as String
    var _age as number

    # ... everything else as-is ...

Quando executado com a opção "-teste" na linha de comando, Cobra executará os testes de unidade contidos na classe, e supondo que todas elas funcionam como esperado, Cobra irá relatar a passagem de testes, bem como compilar o código em um executável. (As bibliotecas são compiladas usando o "-t:lib " opção de linha de comando.)

Isso é uma maneira diferente de criação de testes de unidade, mas ele tem várias vantagens sobre a abordagem tradicional com NUnit testes que os testes são mantidos perto para o código em vez de um arquivo separado onde pode ser muito distante dos desenvolvedores que estão mantendo ou atualizar o código de unidade. Tudo o que torna mais fácil para gravar (e manter) a unidade de testes geralmente é considerado uma coisa boa, e ter os testes de unidade direita dentro da classe para ser testado é um truque há muito tempo aprendeu in the camp Java.

Infelizmente, com essa abordagem para testes vem Inchaço de código; se os testes são suficientemente abrangentes (e eles devem ser), incluindo os testes de verificação e unidade contratuais como parte do código implantado pode dobrar, triplo, ou mesmo quadruple o tamanho do código, que certamente pode colocar um crimp em seus planos para facilitar a implantação do produto finalizado. Felizmente, o idioma Cobra fornece uma solução para isso, por meio do "-turbo " opção, que não só acontece cada otimização, mas também remove o código do projeto-por-contrato e unidade de teste e geralmente serve como uma etapa de otimização apenas antes da implantação.

" É uma sobremesa topping; é uma andar cera. É ambos!"

Anos ou atrás, um skit Live da noite de sábado falamos sobre a versatilidade de um produto que foi um topping sobremesa e uma andar cera. " É ambos!" foi a linha de marca, e no momento, ele foi bastante funny. Ei, era o 70s.

Recentemente, no entanto, um novo tipo de argumento surgiu dentro da comunidade linguagem de programação, de estática versus digitação dinâmica — em outras palavras, estamos volta para os mesmos argumentos que usado para ser redistribuído contra (Clássico) Visual Basic e o tempo de execução (com base em IDispatch) ligação e VARIANTES pela comunidade de desenvolvedores usando C++ e seus recursos de modelo de tempo de compilação. Neste momento, entretanto, é a comunidade/estaticamente digitada na defensiva, argumentando os méritos de segurança em idiomas/estaticamente rigidez, contra os benefícios de produtividade citados por usuários de rubi e Python (ou, se você preferir, IronRuby e IronPython). Como ocorre com a maioria dos debates dessa natureza, há muita rhetoric, muitas declarações unsubstantiated e muita citadas estatísticas (mais deles são compostos no ponto).

Na verdade, o argumento é um pouco falsas e geralmente máscaras uma diferença mais profunda, que chama de mais cedo ou tarde ligação do método. No tradicional" compilado linguagens (como C + c++ / CLI ou C#), o compilador decide no momento da compilação se uma chamada de método é aceitável com base na presença de um método correspondente. Se um for encontrado na classe de destino, ele emite as instruções necessárias (em CIL, essa é uma instrução "callvirt" com o token de metadados do método em questão) no assembly gerado. Por outro lado, uma chamada de ligação tardia não resolvida, na verdade, até o momento execução, normalmente por meio de uma API com base em metadados como reflexão, nesse ponto o tempo de execução procura o método e se houver, chama.

As diferenças aqui não são muito difíceis para especiais — no primeiro caso, o método deve ser visível no momento da compilação, forçando o programador, em alguns casos, para participar de algum tipo de navegação para manter o compilador feliz. (Esse é o universal "se obj estiver pessoa" seguido de um idiom chamada downcast e método.) Essa seqüência específica de declarações de linguagem geralmente é frustrante, especialmente quando o programador "sabe," por devido um maior reconhecimento do contexto da situação, que o objeto em questão é exatamente o tipo necessário fazer a chamada bem-sucedida. Ainda o compilador não pode ver que o momento e força o desenvolvedor através de uma série de etapas simplesmente para acalmar o processo de compilação.

No segundo caso, no entanto, o programador geralmente descobrirá que o que ela "sabe" não jibe exatamente com a realidade, normalmente porque uma exceção de tempo de execução é lançada no máximo constrangedor momento possível — durante a demonstração grande, o lançamento de produto, ou em tempo de execução no dia do IPO. Talvez outro desenvolvedor acidentalmente interrompe que considerado invariável, talvez ele é um caminho de código foi coberto nunca bastante durante testes de unidade, mas independentemente de como isso acontece, ainda deixa o programa finalizado e o programador mortified.

Assim, o argumento persiste: é melhor tirar a produtividade-atingido fazer promessas de compilador assured de correção de código, ou é melhor confiar unidade dos programadores a testar conjunto e informar o compilador para ir travar?

Cobra organizado dodges esse argumento completamente, pelo que está sendo estático e dinâmico. Em outras palavras, Cobra leva a posição que quando ela pode faz isso, ele irá vincular chamadas de método antes, da mesma maneira que optar por C#, mas quando não consegue, por qualquer motivo, ele será optar em vez disso use semântica de ligação tardia e resolver a chamada do método em tempo de execução. E, ao C# 4.0 promete um tipo semelhante de recurso (como, por da maneira, possui Visual Basic como tornou-se uma linguagem .NET, via Option Explicit e Option Strict sinalizadores), Cobra requer não ajuda sintáticas adicional do programador para decidir quando a ser early-bound e quando a ser tardia; simplesmente faz a decisão de como ele compila, organizado evitando a necessidade do desenvolvedor fazer essa chamada antecipadamente, como na Figura 5 .

Vinculação dinâmica a Figura 5

class Person

    get name as String
        return 'Blaise'


class Car

    get name as String
        return 'Saleen S7'


class Program

    shared

        def main
            assert .add(2, 3) == 5
            assert .add('Hi ', 'there.') == 'Hi there.'
            .printName(Person())
            .printName(Car())

        def add(a, b) as dynamic
            return a + b

        def printName(x)
            print x.name  # dynamic binding

Como você pode ver, Cobra oferece suporte tanto o modificador "dinâmico", para indicar que um determinado tipo ou método deve tratar seus argumentos e tipos como dinâmico, bem como a "inferência dinâmica" que é usada no método "printName", onde Cobra simplesmente reconhece que esse nome não pode ser vinculada estaticamente, e, em vez disso, procura resolvê-lo em tempo de execução.

Soma para cima

Conjunto de recursos exclusivos do cobra marca como uma linguagem de "soma das opções" interessante. A opção silenciosa entre inicial e -ligação tardia torna uma linguagem de idéia para trabalhar com componentes .NET que fazem intenso uso de APIs ligação tardia, como o modelo de automação do Office, preservando os benefícios de segurança e desempenho de ligação inicial ainda sempre que possível. Isso sozinho torna Cobra vale a pena considerar, especialmente para Office (e outros baseado em COM) programação de automação.

Quando combinado com a capacidade do Cobra atuar como ferramenta de idioma de scripts e compilados, no entanto, as vantagens do Cobra realmente começam a sobressaia.

Boa sorte com a experimentação Polyglot!

Envie suas dúvidas e comentários para Ted para polyglot@Microsoft.com.

Ted Neward é consultor de segurança com ThoughtWorks, uma consultora internacional especializado em sistemas empresariais confiável e ágil. Ele escreveu vários livros, é um Microsoft MVP Architect, alto-falante INETA e instrutor PluralSight. Alcançar Ted em Ted@tedneward.com, ou ler seu blog em blogs.tedneward.com.