Compartir a través de


El programador Polyglot

Mezclar Y coincidencia de idiomas

Ted Neward

Contenido

Programación políglota
Polyglot en el ejercicio
El coste de las opciones

Mucho antes de la Web y de la programación cliente-servidor, era habitual que una aplicación completa estuviera escrita en un solo idioma y sobre una única plataforma o sistema.Considere, por ejemplo, la plataforma de FoxPro omnipresente, una grapa de programación de aplicaciones para los años. Proporcionan un idioma de formato de la interfaz de usuario y biblioteca, un acceso a datos y formato de almacenamiento, además de un conjunto de rutinas para proporcionar la compatibilidad de biblioteca tradicional, como matemáticas y Contabilidad. Tanto un idioma y una plataforma ajustado en un único entorno, FoxPro fue un ejemplo clásico de cómo un programador puede aprender un lenguaje o plataforma y permanecen correcto y, más importante aún, gainfully empleado.

A continuación, incluida la especialización, nuevos lenguajes y herramientas, cada uno con un propósito determinado y específico. Las bases de datos relacionales se convirtieron en la norma y SQL se convirtió en el idioma utilizado para tener acceso a ellos y modificarlos. Desarrollo de cliente de interfaz gráfica de usuario inicialmente desplaza a procedimientos lenguajes como C y minúscula Pascal y después, con la llegada de orientación de objetos había incluida C++ y Delphi. Herramientas eficaces para administrar un sistema operativo basadas en la consola, sistemas con más frecuencia basados en UNIX, extender las capacidades de shells de comando, como los shells de Korn y de bash se convirtieron en lenguajes como Perl y Python. A continuación, había incluido el Web, y se con él HTML, CSS y JavaScript convirtieron los idiomas de elección para mostrar de la interfaz de usuario.

El mundo de lenguaje de programación nunca ha visto un momento no interactivo, pero en los últimos años, ha expandido un gran número de idiomas en la escena programación, incluidos lenguajes procedimientos como Ruby, Windows PowerShell y Python y funcionales lenguajes como F # y Scala. Idiomas existentes están obteniendo nuevas características, como los datos consultar servicios Microsoft ha agregado a C# con LINQ y ahora ha comenzado una nueva fase de desarrollo de lenguaje de programación en el que se están desarrollando nuevos idiomas personalizadas, conocidas como lenguajes específicos de dominio (DSL), para tareas específicas o aplicaciones. Para obtener un ejemplo de una DSL, vea la columna de estación de servicio de la edición de inicio de Visual Studio de 2008 de MSDN Magazine (msdn.microsoft.com/magazine/cc164250).

Siempre que se activa, nuevos idiomas son sacar hacia arriba. Mientras que los programadores multilingües ciertamente predate, el término "polyglot programación" se ha originado con Neal Ford diciembre de 2006 blog publicar (memeagora.blogspot.com/2006/12/polyglot-programming.html), (no es sorprendente) titulado "Polyglot programación".

Programación polyglot

BUENO, por lo que se ha establecido que hay una gran cantidad de idiomas que existen y hay es probable que un idioma especialmente adecuado para cada problema que deberá dirección. El desarrollador de .NET con debe comprender cómo todos ellos encajan. Será el objetivo de esta columna.

Tenga en cuenta uno de los problemas más comunes hoy en día. Los desarrolladores se se le pide que escalar sus programas, sus sitios Web y servicios, en particular, a un número mayor y mayor de usuarios que nunca. Los clientes que se reciben acceso a su información de cuenta directamente. Esto significa que donde es posible que tengan una aplicación escalar a un par de cientos de usuarios (por ejemplo, para aumentar el número de los usuarios empleado del centro de llamada durante un spurt crecimiento), ahora la misma aplicación debe escalar a posiblemente miles, si no millones de usuarios.

Deben administrar programadores trabajan en el sitio Web conseguir seguridad del subproceso y un buen rendimiento. Basta con bloqueo cada método para serializar el acceso a través de todo el sistema no es una buena solución porque no escalará. Administrar correctamente simultaneidad, especialmente cuando la aplicación de escala, ninguna operación sencilla; mantiene senior incluso a los desarrolladores hasta más tarde por la noche. Incluso con el lanzamiento de nuevas herramientas como PEX (research.microsoft.com/projects/pex) y AJEDREZ (research.microsoft.com/projects/chess) para realizar análisis de código estático y ejecutar pruebas permutaciones de unidades para descubrir los errores de subprocesamiento múltiple, es todavía necesarios para controlar concurrencia y, en un nivel bastante bajo, mediante las instrucciones de "bloqueo" de C# o la simultaneidad distintos de control clases a partir System.Threading.

¿Está pueda asumir este desafío? ¿Una buena forma son sus habilidades de simultaneidad? Rápido, ¿cuál es la diferencia entre System.Threading.Monitor, System.Threading.Mutex y System.Threading.Semaphore?

Aquí debe empezar a ver el valor de una opción de idioma en particular. Quizás este tipo de código complejo sería más fácil escribir y mantener si eligió un lenguaje funcional como F #, cuyos tendencies generales hacia immutability y cero efectos secundarios evitará la necesidad de control de concurrencia explícita, o un idioma específicos de dominio escritos Ruby, que se diseñó para ocultar los detalles de simultaneidad de los programadores utilizarlo.

Para realizar esta idea más concretas, imagine que una aplicación Web tiene que hacer alguna operación sincrónica tradicionalmente, como realizar algunas E/s basadas en archivos (o hacer que una base de datos o servicio Web que llamar a). Normalmente, lo más fácil hacer desde el código de C# será abrir el archivo mediante un tradicional mediante instrucciones, leer el contenido, almacenarlos en una matriz de bytes, cierre el archivo, de manera similar a éste:

byte[] pixels = null
BinaryReader br = 
         new BinaryReader(new FileStream(filename, FileMode.Open)); pixels = br.ReadBytes(4096);

erver" id="tgt36" sentenceid="bf1e46b7b35e3906d0084b66a3ebe870" class="tgtSentence" >Que puede ser sencilla, pero se de serializar también tuerzan. erver" id="tgt37" sentenceid="2368fe64cc43e20915dfaace342ff470" class="tgtSentence" >Ningún procesamiento adicional puede ocurrir en el subproceso mientras está realizando el archivo de operación de E/s. erver" id="tgt38" sentenceid="4a2f89f7a13a865183d7efc038ede329" class="tgtSentence" >(Para operación de E/s de archivos simple uno, probablemente no es una preocupación principal, al menos no hasta que el número de estos Obtiene de las operaciones que puede suceder cuando el sitio intenta escalar muy grande,.) Sería mejor leer dicha archivo mediante las operaciones asincrónicas disponibles a través del grupo de subprocesos CLR, como verá en la figura 1 .

Figura 1 leer archivo de forma asincrónica

delegate byte[] AsyncOpenMethod(string filename);
static byte[] AsyncOpen(string filename)
{
    byte[] pixels = null;
    using (BinaryReader br =
        new BinaryReader(new FileStream(filename, FileMode.Open)))
    {
        Pixels = br.ReadBytes(4096);
    }
}
static void AsyncOpenTheFile(string filename)
{
    byte[] pixels = null;
    AsyncOpenMethod aom = new AsyncOpenMethod(Class1.AsyncOpen);
    IAsyncResult iar = aom.BeginInvoke(filename, null, null);
    while (iar.IsCompleted == false)
    {
        // Do something?
    }
    pixels = aom.EndInvoke(iar);
}

Pero el código que era sencillo antes de que es código ahora que sólo madre un desarrollador podría amor y se claramente ocupa mucho más que simplemente leer un archivo. Ahora examine cómo escribir una rutina similar en F # sería:

async {
use inStream = File.OpenRead(filename)
let! pixels = inStream.AsyncRead(4096)
}

erver" id="tgt41" sentenceid="3e28e8d824e92e475fd06ba701a2190b" class="tgtSentence" >No deseo profundizar demasiado profundamente en cómo el código de F # hace esto, pero la permiten! erver" id="tgt42" sentenceid="6c8ffaa0ad8d896eebbe08722da72034" class="tgtSentence" >expresión indica al compilador F # genera la expresión como una expresión asincrónica, y el método AsyncRead es uno que F # silenciosamente tacks en a las clases derivadas de System.IO.Stream estándar a través de una característica del lenguaje F # denominada métodos de extensión. erver" id="tgt43" sentenceid="0de38b6322de201e5aa1db4e6143f3b9" class="tgtSentence" >Eficazmente lee el archivo de forma asincrónica y Vuelca los resultados en la matriz de bytes en el lado izquierdo de la expresión, todos los sin ningún código más necesario del desarrollador.

erver" id="tgt44" sentenceid="49a196034cea9866f5c9f9d24947ecbf" class="tgtSentence" >Vamos a poner esto en un contexto del mundo real. erver" id="tgt45" sentenceid="65c462054048e89e10c194370136ab7e" class="tgtSentence" >Como parte de un ciclo de mantenimiento por la noche, un servicio Web debe realizar copias de un número de archivos para su fácil almacenamiento sin conexión para fines de copia de seguridad. erver" id="tgt46" sentenceid="610e8c2c701444b429ba88d6b4de3e23" class="tgtSentence" >El código de F # realizar esta copia del archivo, todos los completamente asincrónicamente, parece Figura 2 .

La Figura 2 realizar copia de seguridad de copias

#light

open System.IO

let CopyFileAsync filename =
    async {
        use inStream = File.OpenRead(filename)
        let! pixels = inStream.AsyncRead(4096)
        use outStream = File.OpenWrite(filename + ".back")
        do! outStream.AsyncWrite(pixels)
    }

let tasks = [ for i in 1 .. 10 -> CopyFileAsync("data" + i.ToString()) ]
let taskResults = Async.Run (Async.Parallel tasks)

Aquí, tanto la lectura y escritura se llevará a cabo asincrónicamente, y la colección completa de 10 tareas (uno para cada archivo de datos) también se procesará asincrónicamente. ¿Al tener en cuenta lo que esto tardaría en C#, que en su lugar escribiría? (Coble oportunidad va en enormemente más detalles acerca de F # y asincrónicas programar en" Asincrónico sencillo: crear aplicaciones simultáneas de expresiones de F # simples"en el de 2008 octubre emitir de MSDN Magazine .

Polyglot en el ejercicio

En la práctica, interoperación entre F # y C# (o cualquier otro lenguaje CLR) es relativamente sencillo, una vez que la "forma" del código (qué idioma el convierte en el IL nivel) en ambos idiomas se entiende bien. ¿En la superficie de esto parece bastante fácil; después de todo, cómo diferentes puede una llamada a método ser entre dos idiomas, dado que la especificación de lenguaje común (CLS) indica una gran cantidad de las temas en caso contrario, difícil, como parámetro selección de ubicación, tipos básicos y orden de bytes?

Vamos a poner a la prueba. Tomando algunas sencilla F # código para empezar con, vamos a compilar y convertirlo en un archivo DLL y utilizar ILDasm (el desensamblador de lenguaje intermedio, o reflector, cualquier herramienta piensa más cómodos con) para examinar lo que parece. A continuación, puede ascender a expresiones F # más complicadas, como el código de flujo de trabajo asincrónico Coble oportunidad presenta en su artículo de octubre de 2008, que he mencionado anteriormente.

Para empezar, realizar algún código F # simple

let x = 2

erver" id="tgt59" sentenceid="3aeb9c560e9380ce87cc52f9775773fe" class="tgtSentence" >Si suponemos que reside en el valor predeterminado "biblioteca de F #" proyecto archivo llamado Module1.fs, el IL de erver" id="tgt60" sentenceid="da8f27f5b02db54e691d9d6ff45a1a66" class="tgtSentence" >La figura 3 erver" id="tgt61" sentenceid="66cb5eea4844d126d3ab952fd638d73e" class="tgtSentence" >(con algunas cosas comentarios para mantener la lista de IL breve) se generará para él.

Figura 3 el IL detrás Let x = 2

.assembly Library1
{
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.
  FSharpInterfaceDataVersionAttribute::.ctor(int32, int32,
  int32) = ( 01 00 01 00 00 00 09 00 00 00 06 00 00 00 00 00 ) 

  // ...
}

.class public abstract auto ansi sealed beforefieldinit Module1
       extends [mscorlib]System.Object
{
  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.
  CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.
  FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) 

  .method public static int32  get_x() cil managed
  {
    // Code size       6 (0x6)
    .maxstack  4
    IL_0000:  ldsfld     int32 '<StartupCode$Library1>'.$Module1::x@3
    IL_0005:  ret
  } // end of method Module1::get_x

  .method private specialname rtspecialname static 
          void  .cctor() cil managed
  {
    // Code size       13 (0xd)
    .maxstack  3
    IL_0000:  ldc.i4.0
    IL_0001:  stsfld     native int '<StartupCode$Library1>'.$Module1::_init
    IL_0006:  ldsfld     native int '<StartupCode$Library1>'.$Module1::_init
    IL_000b:  pop
    IL_000c:  ret
  } // end of method Module1::.cctor

  .property int32 x()
  {
    .custom instance void [FSharp.Core]Microsoft.FSharp.Core.
    CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.
    FSharp.Core.SourceConstructFlags) = ( 01 00 09 00 00 00 00 00 ) 
    .get int32 Module1::get_x()
  } // end of property Module1::x
} // end of class Module1

.class private abstract auto ansi sealed beforefieldinit 
    '<StartupCode$Library1>'.$Module1
       extends [mscorlib]System.Object
{
  .field static assembly native int _init
  .custom instance void [mscorlib]System.Runtime.CompilerServices.
   CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  .field static assembly initonly int32 x@3
  .method private specialname rtspecialname static 
          void  .cctor() cil managed
  {
    // Code size       8 (0x8)
    .maxstack  3
    IL_0000:  nop
    IL_0001:  ldc.i4.2
    IL_0002:  stsfld     int32 '<StartupCode$Library1>'.$Module1::x@3
    IL_0007:  ret
  } // end of method $Module1::.cctor

} // end of class '<StartupCode$Library1>'.$Module1

Si el seguimiento del código a través del lenguaje INTERMEDIO, verá dos cosas; en primer lugar, que el nombre "x" desde el código de F # está enlazado en el nivel de CLS como una propiedad estática de una clase denomina Module1 y un segundo, que su valor inicial de 2 no está enlazado como una constante, pero se inicializa cuando el ensamblado se carga, mediante el constructor de tipo (.cctor) de la clase de StartupCode $Library1 generado por compilador. En otras palabras, mientras que podría estar tentado considerar de x como un valor constante que el compilador puede en línea, el compilador decide presentar como una propiedad estática.

Esto significa que, luego, que tienen acceso a este enlace, x, requerirá código de C# algo como lo siguiente:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("F#'s x = {0}", Module1.x);
        }
    }
}

erver" id="tgt65" sentenceid="12541212b8aeed7ac87fc631f8622e91" class="tgtSentence" >Por lo que ahora, por lo que bien. erver" id="tgt66" sentenceid="7dbc613247d1f206a1b5bba43166d9a7" class="tgtSentence" >Pero x es un enlace bastante simple, y como se esperaría que sea una simple cuestión de tener acceso a ella. erver" id="tgt67" sentenceid="4a89e60dbdf8783c6706f09982556140" class="tgtSentence" >Algo un poco más complicado podría presentar problemas más complicados, vamos por lo que a hacer algo sólo un toque más complicado para asegurarse de la asignación de F # a CLS aún hace sentido.

erver" id="tgt68" sentenceid="561ed6faf49a5b271102c8d3e5d2de6c" class="tgtSentence" >Para obtener el código F #

let add a b = a + b

erver" id="tgt69" sentenceid="d61b61fc8b408872f9463b30eea29b01" class="tgtSentence" >el compilador genera el IL adicional en la clase "Module1" en la DLL compilado, F #:

.method public static int32  'add'(int32 a,
                                   int32 b) cil managed
{
  // Code size       5 (0x5)
  .maxstack  4
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  add
  IL_0004:  ret
} // end of method Module1::'add'

erver" id="tgt70" sentenceid="e58ade7103d2daedd385dad271089a6f" class="tgtSentence" >Esto es casi exactamente la versión de C# de la función podría aspecto, para que no existe no mucho que necesita se explican a continuación. erver" id="tgt71" sentenceid="6b661cccd1ac91244bc6475829be4b58" class="tgtSentence" >Al llamar a también es trivial.

erver" id="tgt72" sentenceid="2cbe872236746c803773ec730cb1c770" class="tgtSentence" >Pero uno de los puntos fuertes de F # es que trata funciones como valores de primera clase, y éste pasa a ser más evidentes (y más difícil) al iniciar la creación de las funciones que toman funciones como argumentos, como la siguiente equivalente de la integrada F # biblioteca mapa función, que toma como argumentos de una lista, junto con una función para aplicar a cada elemento de la lista y devuelve una lista nueva que contiene los resultados:

let mymap (l : 'a list) (f : 'a -> 'b) = List.map f l

erver" id="tgt73" sentenceid="01359da1c99a80e777d1bb27b434d790" class="tgtSentence" >Esto es una forma especialmente "funcional" de procesamiento de una lista: en lugar de recorrer en iteración, elemento por elemento, un lenguaje funcional toma una función como un parámetro y se aplica a cada elemento de la lista, generar cualquier un único resultado (denominado una operación de "plegado") o una lista nueva que contiene los resultados de cada uno de ellos (nuestro " mapa").

erver" id="tgt74" sentenceid="d5c878a015d5da98c018ea61214dc029" class="tgtSentence" >Observe en la figura 4 cómo esto convierte en algo bastante compleja de lenguaje INTERMEDIO después de la compilación. erver" id="tgt75" sentenceid="4163a12d5889e5a7bd00f7a666cc3094" class="tgtSentence" >De nuevo, el hecho de que asigna a un método estático público de la clase Module1 no es sorprendente; lo que hará Esto difícil interactuar con de C#, sin embargo, es que el método estático toma listas F # (es decir, instancias de tipos de parámetros de Microsoft.FSharp.Collections.List) como tipos de entrada y de valor devueltos, junto con una función (significado, una instancia de una instancia dually-tipo de parámetros de Microsoft.FSharp.Core.FastFunc) como la segunda entrada.

Figura 4 el IL por la función de asignación

  .method public static class [FSharp.Core]Microsoft.FSharp.Collections.
       List'1<!!B> 
          mymap<A,B>(class [FSharp.Core]Microsoft.FSharp.Collections.
              List'1<!!A> l, class [FSharp.Core]Microsoft.FSharp.Core.
                         FastFunc'2<!!A,!!B> f)
          cil managed
  {
    // Code size       11 (0xb)
    .maxstack  4
    IL_0000:  nop
    IL_0001:  ldarg.1
    IL_0002:  ldarg.0
    IL_0003:  tail.
    IL_0005:  call       class [FSharp.Core]Microsoft.FSharp.Collections.
       List'1<!!1>
       [FSharp.Core]Microsoft.FSharp.Collections. 
       ListModule::map<!!0,!!1>(class [FSharp.Core]Microsoft.
       FSharp.Core.FastFunc'2<!!0,!!1>,
       class [FSharp.Core]Microsoft.FSharp.Collections.List'1<!!0>)
    IL_000a:  ret
  } // end of method Module1::mymap

Esto se va a requerir algún código C# grave para que funcione correctamente. Para este ejemplo, algunos C# código vaya a admite una colección de enteros y convertirlos en sus formularios de la cadena correspondiente. (El hecho de que podría hacer esto bastante fácilmente desde dentro de C# es irrelevante aquí, se debe averiguar cómo realizar las acciones sencillas antes de puede enfrentarse a los más complejos.) Para llamar a este desde C# significa que varias cosas van a tener que producen correctamente: se necesita la colección de entrada se convertir en un tipo de lista F #; la función que se aplica a cada elemento se debe convertir en una F # "FastFunc" instancia; y la lista de F # devuelta tendrá que se conviertan en un tipo C# puede utilizar, o bien utilizar en su formulario de F # nativo directamente.

El código de C# se va a necesita esos tipos de F #, y por lo que será el primer paso agregar la correspondiente F # ensamblado de referencia, en este caso, FSharp.Core.dll. Construir una lista de F #, sin embargo, es no como creando una lista de C#, en lugar de pasar de la colección de C# a través de un constructor, F # asume que las listas se han generado hasta utilizando el operador "cons", que es un método estático en la clase de lista F # < >. En otras palabras, el código de F #

let l1 = [1; 2; 3;]

erver" id="tgt82" sentenceid="b08a656206567262ee96050d2505fc0a" class="tgtSentence" >Convierte en el IL feas en su lugar se muestra en erver" id="tgt83" sentenceid="0004ec1c38122707ab3659a12a084f75" class="tgtSentence" >La figura 5 .

La figura 5 permitir l1 = [1; 2; 3;]

  IL_0000:  ldc.i4.1
  IL_0001:  ldc.i4.2
  IL_0002:  ldc.i4.3
  IL_0003:  call       class [FSharp.Core]Microsoft.FSharp.Collections.
List'1<!0> class [FSharp.Core]Microsoft.FSharp.Collections.
List'1<int32>::get_uniq_Empty()
  IL_0008:  newobj     instance void class [FSharp.Core]Microsoft.FSharp.
Collections.List'1/_Cons<int32>::.ctor(!0,
              class [FSharp.Core]Microsoft.FSharp.Collections.List'1<!0>)
  IL_000d:  newobj     instance void class [FSharp.Core]Microsoft.FSharp.
Collections.List'1/_Cons<int32>::.ctor(!0,
              class [FSharp.Core]Microsoft.FSharp.Collections.List'1<!0>)
  IL_0012:  newobj     instance void class [FSharp.Core]Microsoft.FSharp.
Collections.List'1/_Cons<int32>::.ctor(!0,
              class [FSharp.Core]Microsoft.FSharp.Collections.List'1<!0>)

Esto significa que para convertir una matriz de C# en una lista de F # adecuada para pasar al método mymap, debe repetidamente llamar al método DIA estático en la lista, pasando en el nuevo central cada vez; se observe que porque el elemento agrega va al encabezado de la lista, para mantener la lista de F # en el mismo orden que la matriz de C#, debe iniciar al final de la matriz y trabajo hacia atrás:

int[] scores = {1, 2, 3, 4, 5};

var fs_scores = Microsoft.FSharp.Collections.List<int>.get_uniq_Empty();
for (int i = scores.Length-1; i >= 0; i--)
{
  fs_scores = Microsoft.FSharp.Collections.List<int>.Cons(scores[i],
                                                          fs_scores);                
}

erver" id="tgt85" sentenceid="948843d179e2235a727b7b931eb38814" class="tgtSentence" >Ya esto se está convirtiendo algo de una dificultades. erver" id="tgt86" sentenceid="342ee9f73a363ea915afa00bc9531e8d" class="tgtSentence" >La obtención de una instancia del tipo de F # FastFunc es incluso más tedioso porque la FastFunc tipo, como el principal tipo System.Delegate, no es realmente pretende pueden crear instancias por los programadores, aunque en su lugar controlado por el compilador F #. erver" id="tgt87" sentenceid="3f88fdb06060735ff9cd9d27306040f5" class="tgtSentence" >Normalmente, al construir una instancia de éste en el código de F #, el compilador realmente genera una clase interna que se hereda de FastFunc, algo que podría hacer de C#, pero que requiera mucho más trabajo.

erver" id="tgt88" sentenceid="b5bd662e1701242afc07f69cbed1a76f" class="tgtSentence" >De repente llamar a esta función F # parece demasiado trabajo para el beneficio derivado de forma.

erver" id="tgt89" sentenceid="db4098edad89b458b3499287b0e72ba4" class="tgtSentence" >Qué todo esto sirve para ilustrar es que la asignación de un idioma a su plataforma subyacente es crucial para decidir dónde y cómo usar lenguajes de una manera polyglot. erver" id="tgt90" sentenceid="5beba265953378873ee91e189dc1dabb" class="tgtSentence" >Algo que es trivial para realizar dentro de F #, por ejemplo, puede activar fuera sea difícil realizar directamente desde C#. erver" id="tgt91" sentenceid="2509702bcf81d300504d97cf15dd965b" class="tgtSentence" >Esto no significa dar hacia arriba en la idea de polyglotism, simplemente significa que debe elegir cuidadosamente los idiomas que utiliza conjuntamente y para los propósitos. erver" id="tgt92" sentenceid="5c630c524d5ee6d53be998e43c15ec30" class="tgtSentence" >Desde una perspectiva más positiva, considerar, uno de los ejemplos de artículo de octubre de oportunidad con código adicional en la parte inferior (consulte la figura 6 )

Figura 6 el código adicional modificaciones original

open System
open System.IO
open Microsoft.FSharp.Control.CommonExtensions

#nowarn "057"
let aCountSpace filename size = 
  async {
     let space = Convert.ToByte ' '
     use stream = File.OpenRead (filename)
     let bytes = Array.create size space
     let! nbytes = stream.ReadAsync (bytes,0,size)
     let count = 
       bytes
       |> Array.fold_left (fun acc x -> if (x=space) then acc + 1 else acc) 0
     return count
  }

let aCounted (files : FileInfo array) = 
    files
    |> Array.map (fun f -> aCountSpace (f.FullName) (int f.Length))
    |> Async.Parallel 
    |> Async.Run

Tenga en cuenta que ha modificado se ligeramente. No obstante, la única modificación realizados aquí era especificar el argumento "archivos" en el método de "aCounted" para ser una matriz de FileInfo, algo que se deduce por el compilador F # en código original del oportunidad. La firma de lenguaje INTERMEDIO de "aCounted" es ahora similar

 .method public static int32[] aCounted(class [mscorlib]System.IO.FileInfo[] files) cil managed

erver" id="tgt96" sentenceid="8fb459965217e77a2f871e9615083914" class="tgtSentence" >lo que hace bastante trivial para llamar a de C#, como en:

int[] fileCounts = 
  Module1.aCounted(new DirectoryInfo(@"C:\Projects\Test").GetFiles("*.txt"));
foreach (var ct in fileCounts)
{
    Console.WriteLine("Count = {0}", ct);
}

Todavía esta nueva versión, tan sencilla como lo es llamar a, conserva la capacidad de ejecución asincrónica completa que tratan de oportunidad nuevo en octubre.

Tenga en cuenta que el uso de C# aquí es meramente arbitrario, basado completamente en el hecho de que sientan más cómodo en C# que en Visual Basic o C + c++ / CLI; dicho que, no debería resultar difícil ver cómo el mismo código que presentarían en cualquiera de esos idiomas.

El coste de las opciones

Siempre hay un costo para adoptar un enfoque polyglot, por supuesto. La primera preguntas principales es bastante obvio para ver, dado que ya se ha ejecuta en él, los desarrolladores que deseen realizar uso de distintos idiomas en un proyecto debe entender cómo se asignan los idiomas a la plataforma subyacente y posteriormente entre sí. Esto significa que basta con confiar en el compilador para hacer cosas derecho no funcionará.

Los desarrolladores se necesite ver cómo el compilador asigna las construcciones de lenguaje a la plataforma subyacente (en este caso, el CLR), y cómo otros idiomas se recoja los construcciones. La buena noticia deriva del hecho de que idiomas no cambian muy a lo largo del tiempo, incluso para idiomas que han visto algunos bastante radicales turnos a través de la última década, como C# o Visual Basic, por lo que una vez que ha aprendido algunas de las asignaciones de idioma, esta información permanece relativamente constante.

El segundo problema deriva de la primera: Depurar un programa polyglot puede ser más difícil de depurar una monoglot uno, debido a simplemente el hecho de que ahora el desarrollador debe hablar en dos o más idiomas, lugar sólo el. En el caso del código que ha presentado, por ejemplo, depuración requerirá establecer un punto de interrupción en el código de C# y paso a paso, potencialmente en el código F # y vuelva a fondo. En muchos aspectos, esto es nada nuevo, F # es sólo un idioma más en la lista de idiomas que necesita saber.

En algunas tiendas de desarrollo, los programadores trabajan en C# código simplemente confianza (ya sea por declaración o a través de FE en pruebas de unidades) que el código de F # funciona como anunciar y paso a través en vez de paso en el código de F #, igual que muchos desarrolladores de Visual Basic y C# hace al paso a paso el código que llama en las bibliotecas de C o C++ no administradas de.

En el futuro las columnas, veremos otras formas de enfoques polyglot que más fácil y en otros idiomas y sus ventajas para el programador polyglot de programación.

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.