Compartir a través de


Este artículo proviene de un motor de traducción automática.

El programador Polyglot

Reaping las ventajas de Cobra

Ted Neward

Contenido

Comienzo
Confianza pero comprobar
" Es un postre topping; se trata un lacre de planta. Es ambos!"
Sumar hacia arriba

En la primera entrega de esta columna, he explicado la importancia de descripción de más de un lenguaje de programación y en particular, más de un tipo de programación de idioma; simplemente saber C# o Visual Basic no es suficiente para que resalte contra la mob ya. Las herramientas más en el cuadro de herramientas, mejor será el carpintero se convierte en. (Copia a un punto, todos modos, ' tis la mala carpintero que posee cada herramienta realizada alguna vez y aún aún así no sabe cómo utilizar alguna de ellas.)

En esta entrega, examinaré un idioma que no demasiado se quitará de está familiarizado con los idiomas de orientado a objetos, pero tiene unas nuevas características que no sólo se ofrecen algunas nuevas ideas, pero posiblemente el establecido de algunas nuevas formas de pensar acerca de la herramienta actual de iniciar.

Permitirme para presentarle Cobra, un descendiente de Python que, entre otras cosas, ofrece un combinado dinámico y estática con modelo de programación, instalaciones de prueba de unidad integrado, las capacidades de secuencias de comandos y algunas declaraciones de diseño por contrato.

Comienzo

Como Lewis Carroll es famoso para suele decir, permítanos "comenzar al principio". Cobra es un lenguaje .NET que se puede obtener mediante el explorador Web, en cobra-language.org, o mediante un cliente de daños en el mismo dominio. Si creando de origen, utilice la secuencia de instalación de workspace.bat en el directorio de origen para generar el compilador Cobra e instalar los ensamblados necesarios en la caché de ensamblados global (GAC). Aunque la creación de origen no es necesario, también están disponibles los binarios integrados, a menudo resulta interesante observar el origen del compilador del Cobra, que está escrito en Cobra (un hito importante en cualquier idioma), como una manera de ver muchas, si no, de las características del lenguaje en el trabajo en un programa realista.

Una vez que se genera el compilador, en el primer paso se es probar la el idioma Cobra a través del todo el personal favorito "Test-la-del compilador" (o como una es "prueba el programador") el omnipresente Hello World, programa:

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

Cobra ofrece dos modos diferentes de interactuar con su código de origen: en el código se puede ejecutar directamente a través de una secuencia de comandos, como el enfoque al ejecutar "Hello.cobra cobra" desde la línea de comandos o puede ser el código compilado y, a continuación, ejecutada de la forma tradicional de C# y Visual Basic y C++ idiomas, por, al compilar, primero ("cobra –compile Hello.cobra") y ejecutarla como lo haría con cualquier archivo ejecutable de .NET. Tenga en cuenta que el ejecutable de "cobra" es la misma en ambos casos, uno integrado en el directorio Cobra\Source. (Observador lectores que examinar el contenido del directorio después de "Scripting" el archivo .cobra se observe que modo de "secuencias de comandos" del Cobra es realmente sólo una secuencia de compilación, a continuación, ejecutar de dos pasos, dejando compilado "Hello.exe" detrás después de terminado de ejecutarse.)

Examinar el origen Cobra, resulta evidente que aunque hay algunas similitudes distintos a los lenguajes C# y Visual Basic, hay también algunas diferencias inequívocas. De hecho, los lectores familiarizados con Python (o su equivalente CLR, IronPython) se rápidamente tenga en cuenta que Cobra principalmente se deriva la sintaxis Python, del Cobra inventor, Chuck Esterbrook, encuentra la sintaxis Python atractivo y que utilizado como base desde el que se derive su idioma.

Para aquellos que no ha utilizado nunca Python antes, sin embargo, una diferencia inmediatamente destaque: en lugar de utilizar palabras clave (como "inicial" y "final") o símbolos (tokens) (como "{" y"}") para establecer los bloques de código aparte desde el código que lo rodea, Cobra, como Python, utiliza el espacio en blanco significativo o, como inventor del Cobra prefiere, bloques, que significa que bloques de código se establece una sangría separados por sangría léxico. En otras palabras, al final de un bloque de toma de decisiones (un bloque "si") está señalado no forma una llave de cierre, pero por el hecho de que la siguiente línea es un nivel de sangría quita de la línea anterior. Por lo tanto, en la figura 1 , Cobra sabe que la última línea "Imprimir" se ejecuta independientemente de los resultados de la prueba "si", porque se aplica sangría esa línea igualmente a la instrucción "si" que tomado la decisión.

Figura 1 Estructura de sangría en el código de 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"

Causa de los problemas que surgen si mezcla las fichas y espacios en un único archivo para controlar la sangría, Cobra mandatos bien y o, pero no ambos, generar un error del compilador que ve una mezcla de los dos.

Además, Cobra ha codificadas tendencies ritualistic el nombre de la claridad del código:

  • Tipos, como clases, estructuras, interfaces y enumeraciones, debe estar en mayúsculas y no se puede iniciar con un carácter de subrayado, similar a los patrones de establecidos por Microsoft en la biblioteca de clases base: valor de tipo String, control, el dominio de aplicación y así sucesivamente.
  • Los argumentos y las variables locales deben comenzar con una letra en minúsculas y no pueden comenzar por un carácter de subrayado. Algunos ejemplos son x, y, número y índice.
  • En las expresiones, acceso a miembros de objeto o una clase siempre se realiza explícitamente con un punto (.foo) o implícitamente si el miembro se inicia con un carácter de subrayado ("_foo").
  • Aunque no se aplica, variables de objeto llevan el prefijo normalmente por un carácter de subrayado, que también implica visibilidad protegida. Algunos ejemplos incluyen _serialNum, _color y _parent.

Por lo tanto, para cualquier determinado fragmento de código Cobra, un programador Cobra puede distinguir fácilmente los elementos implicados en el fragmento o fragmento de código. Por ejemplo, en la siguiente instrucción Cobra

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

se puede saber

  • e y del alfabeto son las variables locales o argumentos
  • _random es un objeto miembro o variable, de visibilidad protegida (probablemente)
  • Elemento es un tipo

Cobra sigue, por tanto, la premisa básica de Python: que para cualquier situación determinada, hay sólo una manera de hacerlo, incluido el código de nombres y el formato convenciones.

Confianza pero comprobar

Confianza pero comprobar. Estas palabras, famoso uttered por Estados Unidos anterior Presidente Ronald Reagan (en el contexto de negociaciones de reducción de segmentos), mantenga sólo como encarecidamente para programar que lo hacían para reducción de los brazos nuclear. Aunque es fácil de creer y la confianza, que los programadores de otros sitios de trabajo podrían siempre saben mejor que pasa una referencia nula en un método que documenta claramente el hecho de que hacerlo, generará una excepción NullReferenceException, es generalmente ha demostrado una y otra vez (especialmente durante demostraciones antes del cliente o el jefe grandes) que el curso más seguro es hacer que los supuestos explícita. De hecho, el presidente primero hubiera activo hoy en día y rellena la programación, es totalmente posible que podría modificar sus palabras para leer, "confianza, pero el documento, insisten en y compruebe."

En el mundo de la programación, esto significa que dos aspectos básicos: que insisten en es para programar defensively, con frecuencia mediante afirmar instrucciones o métodos para garantizar (con el riesgo de una excepción en tiempo de ejecución) que los valores pasan de coinciden ciertos criterios; para comprobar escribir pruebas unitarias con el método para garantizar que la implementación del método controla el programador pathological que insists en infringir restricciones de las.

Consideremos la tarea relativamente sencilla de crear una clase que representa una persona dentro del sistema. Las personas normalmente tienen un nombre, un apellido y una edad y pueden ser casado o casadas a otras personas. En general, al escribir estos tipos de clases, deberá establecer algunas constantes, los principios básicos que contendrá para cualquier uso de la clase. Por lo tanto, por ejemplo, una clase Person puede decidir que un nombre de último no puede ser nulo, algo que se suelen impuesta por el desarrollador escribir persona y normalmente se realiza en la construcción del conjunto de propiedades de la clase, como verá en el código de C# en la figura 2 .

La Figura 2 invariables Forzar restricciones

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: 
  }
}

El problema con esto es que se exige la invariable sólo cuando se utiliza la propiedad de Set; si otro desarrollador viene alrededor más adelante coloca un nuevo método de la persona que directamente se hace referencia el almacén de respaldo (el campo encapsulado por la propiedad, la invariable, por tanto, se rompe modo silencioso y sutilmente y nadie observará hasta que sea demasiado tarde. (Esto es el corazón del debate alrededor de si las clases deben utilizar propiedades internamente o manipular directamente los campos.) Se esta pregunta, por supuesto, aplica igualmente tanto como propiedades a los métodos. Aunque no es a menudo que una propiedad toca respaldo más de un campo, no está prohibido por el lenguaje, y ocurra periódicamente. De mayor preocupación, invariable cada uno de los (nombre no puede ser nulo ni estar vacío, último nombre no puede ser nulo ni estar vacío, edad no puede ser negativo) tendrá que se aplica, en cada método, propiedad o constructor que potencialmente podría modificar el estado interno.

Cobra ofrece un sistema básico de diseño por contrato, en que una clase o método puede declarar ciertos requisitos acerca de la clase y el compilador Cobra se silenciosamente agregar instrucciones de comprobación para cada uno de los miembros (propiedad o método) que puede modificar potencialmente los datos en cuestión. De este modo, se garantiza que apellido nunca es null, independientemente de cómo se utiliza la clase y sin tener que codificar explícitamente en cada método que, posiblemente, puede cambiar el contenido de la instancia (consulte la figura 3 ).

Instrucciones de comprobación de la 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'

Es relativamente fácil comprobar exactamente cuánto y con qué frecuencia el idioma Cobra inserta estas comprobaciones de validación, desencadenar hasta el desensamblador de lenguaje INTERMEDIO (ILDasm.exe) y examinar la clase Person resultante. Realizar por lo que revela la belleza del enfoque Cobra, cada método de "modificar" se anexa con el código para comprobar las invariables, haciéndolos todo de clase. (Hay una opción del compilador para cambiar cómo se generan las constantes, no en absoluto, o sólo en los métodos o, el valor predeterminado, anexa a todas estas operaciones.)

En y de sí misma, la característica de diseño por contrato de Cobra es interesante, pero es todavía no suficiente para garantizar programas libre de errores; Además, casi siempre es necesario escribir una serie de pruebas unitarias con el código para asegurarse de que se comporta como desee en datos correctos y erróneos se pasan. De nuevo, dado que esto es tan importante para el proceso de desarrollo de software, Cobra decide hacerlo en una construcción de participante en el idioma, construir una "prueba" del lenguaje, como que Cobra puede ejecutar las pruebas como parte del proceso de compilación. Por lo tanto, por ejemplo, un conjunto sencillo de las pruebas unitarias puede aspecto Figura 4 de la clase Person.

Figura 4 pruebas unitarias de persona

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 ...

Cuando se ejecuta con la opción "-probar" en la línea de comandos, Cobra ejecutará las pruebas unitarias contenidas en la clase, y suponiendo que todas ellas funcionan según lo previsto, Cobra se informe de la fase de pruebas, así como compile el código en un archivo ejecutable. (Las bibliotecas están compiladas con la "-t:lib " modificador de la línea de comandos.)

Esto es una forma diferente de creación de las pruebas unitarias, pero tiene varias ventajas sobre el enfoque de en función de NUnit tradicional para las pruebas en que las pruebas se mantengan cerca en el código en lugar de en un archivo independiente donde puede obtener demasiado lejos de los desarrolladores que se mantienen o actualizar el código de unidades. Todo lo que facilita a escribir (y mantener) la unidad de pruebas es generalmente considera algo bueno y tener las pruebas unitarias derecha dentro de la clase a probarse es un truco hace tiempo ha aprendido en el campo de java.

Por desgracia, con este enfoque para pruebas incluye código sobrecargar; si las pruebas son lo suficientemente completas (y deben ser), incluidas las pruebas de comprobación y unidad contractuales como parte del código implementado puede doble, triple, o incluso cuatro el tamaño del código, que ciertamente, puede colocar un crimp en los planes de implementación sencilla del producto terminado. Afortunadamente, el idioma Cobra proporciona una solución a esto, a través de la "-turbo " opción, que no sólo resulta cada optimización, pero también se elimina el código de prueba de diseño por contrato y unidad y normalmente está pensado como un sólo de antes de paso de optimización de implementación.

" Es un postre topping; se trata un lacre de planta. Es ambos!"

Mayores, hace un skit Live sábado noche hablado la versatilidad de un producto que era una topping postre y un lacre de planta. " Es ambos!" estaba la consigna y en el momento, muy divertido. Bueno, es el 70.

Recientemente, sin embargo, ha surgido una nueva opción del argumento de la comunidad lenguaje de programación, de estático frente a escribir dinámico, en otras palabras, estamos a los mismos argumentos que utiliza para redistribuir frente a (básica) de Visual Basic y su tiempo de ejecución (en función de IDispatch) enlace y VARIANTES por la comunidad de desarrolladores con C++ y sus instalaciones compile-time plantilla. Sin esta vez, embargo, es la y estáticamente con establecimiento inflexible de tipos comunidad el defensive arguing las ventajas de seguridad en idiomas y estáticamente con establecimiento inflexible de tipos, frente a las ventajas de productividad citadas por usuarios de Ruby y Python (o, si lo prefiere, IronRuby y IronPython). Al igual que con la mayoría de los debates de esta naturaleza, hay mucha rhetoric, gran cantidad de solicitudes unsubstantiated y muchas citas estadísticas (más de los cuales están formados por en la zona).

En realidad, el argumento es un poco falsos y a menudo enmascara una diferencia más profunda, que de pronto o tarde enlace del método llama a. En "tradicional" compilados lenguajes (como C++ / CLI o C#), el compilador decide en tiempo de compilación si una llamada al método es aceptable según la presencia de un método coincidente. Si uno se encuentra en la clase de destino, emite las instrucciones necesarias (en el código CIL, esto es una instrucción "callvirt" con el símbolo (token) de metadatos del método en cuestión) en el ensamblado generado. En cambio, una llamada enlazada en tiempo de ejecución no resuelve realmente hasta momento ejecución, normalmente mediante una API en función de metadatos como reflejo, momento en que el tiempo de ejecución busca el método y si está presente, invoca se.

Las diferencias aquí no son demasiado difíciles de manchas de color, en el primer caso, el método debe ser visible en tiempo de compilación, forzando al programador, en algunos casos, participar en algún tipo de exploración para mantener el compilador feliz. (Esto es la información omnipresente "si obj es persona" seguido de una forma de llamada convertirlo y método.) Esta particular secuencia de instrucciones de lenguaje a menudo es frustrante, especialmente cuando el programador "sabe," en virtud de un mayor conocimiento del contexto de la situación, que el objeto en cuestión es exactamente el tipo que lo necesario realizar la llamada se realice correctamente. Aún el compilador no puede ver ahora y obliga al programador a través de una serie de pasos simplemente a appease el proceso de compilación.

En el segundo caso, sin embargo, los programadores a menudo descubrirá que lo "sabe" no jibe exactamente con la realidad, normalmente porque se produce el máximo embarrassing momento posible una excepción de tiempo de ejecución, durante la demostración grande, el lanzamiento de producto, o en ejecución momento en el día de la IPO. Quizá otro desarrollador accidentalmente afectaba que supone invariable, tal vez es una ruta de código que se cubrió nunca bastante durante las pruebas unitarias, pero independientemente de cómo ocurre, sigue deja el programa finaliza y el programador mortified.

¿Por lo tanto se conserva el argumento: es lo mejor aprovechar la productividad presionar para obtener promesas seguro por el compilador de código de corrección, o es mejor confiar en la unidad de los programadores probar conjunto y indicar al compilador que vaya bloquee?

Cobra claramente dodges este argumento completamente, al que se estáticos y dinámicos. En otras palabras, Cobra toma la posición que si puede hacerlo, enlazar las llamadas a métodos pronto, de la misma manera que decide C#, pero cuando no, por cualquier motivo, le elegir en su lugar utiliza semántica enlazada en tiempo de ejecución y resolver la llamada de método en tiempo de ejecución. Y, mientras 4.0 de C# promete un tipo similar de capacidad (que, por el modo tenga Visual Basic ya que se convirtió en un lenguaje .NET, mediante la opción explícita y indicadores Option Strict), Cobra no requiere ninguna ayuda adicional sintáctica desde el programador para decidir cuándo se enlace en tiempo de compilación y cuándo se enlazada en tiempo de ejecución; simplemente toma la decisión que se compila, claramente evitando la necesidad para el desarrollador realizar esa llamada antelación, como en la figura 5 .

La figura 5 enlace dinámico

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 se puede observar, Cobra admite el modificador "dinámico", para indicar que un determinado tipo o método debe tratar sus argumentos y tipos como dinámico, así como la "inferencia dinámica" que se utiliza en el método "printName", donde Cobra simplemente se reconoce que ese nombre no puede estar enlazado estáticamente, y en su lugar, intenta solucionarlo en tiempo de ejecución.

Sumar hacia arriba

Conjunto de características exclusivos del cobra lo marca como un lenguaje "suma de opciones" interesante. El modificador silencioso entre anticipado y Límite de enlace facilita un idioma idea para trabajar con los componentes de .NET que uso elevado de API enlazada en tiempo de ejecución, como el modelo de automatización de Office, conservando todavía las ventajas de seguridad y rendimiento de enlace en tiempo de diseño que sea posible. Esto sólo hace Cobra merece la pena tener en cuenta, especialmente para Office (y otros basadas en COM) programación de automatización.

Cuando se combina con capacidad del Cobra de actuar como herramienta de lenguaje de las secuencias de comandos y compilado, sin embargo, las ventajas del Cobra realmente empezar a solar.

Buena suerte con la experimentación Polyglot!

Envíe sus preguntas y comentarios para Ted a polyglot@Microsoft.com.

Ted Neward es un consultor principal con ThoughtWorks, un consultancy internacional especializado en sistemas empresariales confiables, ágil. Ha escrito varios libros, es un arquitecto de MVP de Microsoft, INETA orador y instructor de PluralSight. Llegar a Ted en Ted@tedneward.com, o leer su blog en blogs.tedneward.com.