martes 25 de noviembre de 2008

PDC 2008: The Future of C# 4.0 (2/3)

2 Comentarios

Seguimos comentando la presentación de Anders Hejlsberg sobre el futuro de C# y las novedades que nos aguardan para la próxima versión.

Ahora le toca el turno a una característica que los usuarios de VB.NET disfrutan también desde siempre y que personalmente no entiendo cómo ha tardado tanto en aparecer en C#: Llamadas a métodos con valores por defecto para los parámetros.

Si has tocado C++ seguramente has echado de menos escribir algo como:

void SomeMethod(string connectionString, int timeout = 1000) {...};
...
SomeMethod("myConnectionString"); //1000ms timeout
SomeMethod("otherConnectionString", 3000);

Esta limitación se puede solventar fácilmente, simplemente escribiendo varias sobrecargas para el método:

void SomeMethod(string connectionString , int timeout = 1000) {...};
void SomeMethod(string connectionString):this (connectionString, 1000) {...}

SomeMethod("myConnectionString"); //1000ms timeout
SomeMethod("otherConnectionString", 3000);

Pero escribir más sobrecargas implica... escribir más código. Eso es algo que el propio compilador podría hacer y que de hecho ya hacía en otros lenguajes, por lo que realmente era una carencia ... curiosa.


Quizá para implorar el perdón de las hordas, además de prometer que a partir de ahora se intentaría que ambos lenguajes (VB.NET y C#) evolucionen de forma paralela para mantener en ambos funcionalidad similar, se añadirá la posibilidad de utilizar parámetros con nombre dentro de métodos con parámetros por defecto. Eso implica que ahora podremos elegir qué parametros por defecto utilizar, y no estaremos limitados a utilizarlos en el orden declarado. Por ejemploFusilando de nuevo, si tenemos

public StreamReader OpenTextFile(
string path,
Encoding encoding = null,
bool detectEncoding = true,
int bufferSize = 1024);
Podríamos llamarlo así:
OpenTextFile("C:\mypath");
OpenTextFile("C:\mypath",Encoding.ASCII);
OpenTextFile("C:\mypath",Encoding.ASCII, false);
OpenTextFile("C:\mypath",Encoding.ASCII, false, 2048);
Pero no podríamos llamar a dicho método así:
OpenTextFile(2048);

Es decir, el orden de uso de los parámetros depende de su declaración. De hecho todos los parámetros por defecto deben ser los últimos que se declaren (los que estén "más a la derecha" en la firma del método)


Sin embargo con parámetros por nombre podremos definir el nombre del parámetro que queremos establecer y el compilador utilizará los valores por defecto para el resto si procede. Así, ahora podríamos hacer:

OpenTextFile(bufferSize : 2048, path :"C:\MyPath");
Siendo esta llamada equivalente a
OpenTextFile("C:\MyPath", null, true, 2048);

Por supuesto los parámetros que no tienen un valor por defecto DEBEN aparecer en la llamada. Además los parámetros con nombre se evaluarán en el orden en que aparezcan en la llamada, y no en el orden en el que estén definidos.


Para la próxima entrada comentaremos la última novedad, algo por lo que también hubiera matado pagado: covarianza y contra-varianza con generics.

domingo 23 de noviembre de 2008

PDC 2008: The Future of C#4.0 (1/3)

0 Comentarios

Hace aproximadamente un mes se celebró la Professional Developers Conference 2008, conferencias dedicadas principalmente a desarrolladores Windows. En esta edición se han presentado múltiples novedades, como el SDK de Microsoft Surface (la pena es que me parece que para conseguirlo hay que comprarse también el aparatito, que no es precisamente barato), han presentado novedades sobre Windows 7, y muchas otra cosas más. Podéis hacer una búsqueda para intentar encontrar una conferencia que contenga un tema que os interese. Las vídeos de las conferencias pueden verse por streaming -siempre que tengas instalado Silverlight- pero también están disponibles para descarga en múltiples formatos; y lo mismo se aplica a las presentaciones utilizadas.

Después del párrafo patrocinado por Microsoft :) vamos a realizar una pequeña serie de posts comentando la presentación que da título a esta entrada y que hasta hoy mismo no he podido mirar: The future of C#, presentada por Anders Hejlsberg, arquitecto jefe del desarrollo del lenguaje C#.

Anders comienza reseñando que las nuevas "tendencias" en programación apuntan a lenguajes de programación declarativos, dinámicos y con soporte para concurrencia, indicando que C# se mueve hacia dichas tendencias:

  • Programación declarativa: LINQ nos permite especificar los datos que queremos y cómo los queremos, pero la forma última de acceder a ellos y tratarlos no es de nuestra competencia. La ventaja que esto puede tener es que el compilador JIT podría realizar optimizaciones según el tipo de dato al que accedemos.
  • Lenguajes dinámicos: aunque defensor de los lenguajes y del tipado estático, reconoce muchas virtudes de utilizar un lenguaje dinámico en muchas situaciones. Además, una de las ventajas que tienen los lenguajes dinámicos es la capacidad de metaprogramación. Estos dos aspectos se intentarán mejorar en el lenguaje mediante la palabra clave dynamic y la presentación del compilador de C# como servicio (en próximas entradas :) .
  • Concurrencia: Aquí ya no hay otro truco que el diseñar la aplicación como un grupo de trabajos independientes y paralelizables. Sin embargo hay ya una extensión al lenguaje llamada .NET Parallel Extensions de la que ya hay disponible una CPT para su descarga.



Pasemos entonces a comentar el primero los añadidos más interesantes de C# presentados:

Programación dinámica: palabra clave dynamic

La ejecución de código dinámico no es algo nuevo en .NET, ya hay un esfuerzo para definir un entorno de ejecución dinámico común en todo .NET: el DLR, que estandariza el sistema de tipos, de enlace dinámico y de generación dinámica de código, de forma que sea accesible para cualquier lenguaje y además permita que las diferentes implementaciones interactúen entre sí de forma más cómoda. Por ejemplo, tanto IronRuby como IronPython, utilizan el DLR. Vamos, que es algo así como el CLR pero en dinámico.

DLR

El propósito de la palabra clave dynamic es "comunicarse con cosas que no son una clase .net con tipado estático". Esto se realiza por medio de binders a dichos lenguajes, y  como se ve en la figura, entre esas cosas se incluyen propio código .NET enlazado dinámicamente, java script, Python, Ruby, o bien objetos COM. De hecho esta palabra clave está llamada a dejar obsoleta mi librería LateBindingHelper. Por ejemplo Fusilando este código de la presentación, supongamos un objeto Calculator que permite realizar operaciones aritméticas:

Calculator calc = GetCalculator();
int sum = calc.Add(10, 20);
Si queremos utilizarlo usando LateBinding:
object calc = GetCalculator();
Type calcType = calc.GetType();
object res = calcType.InvokeMember("Add",BindingFlags.InvokeMethod, null,new object[] { 10, 20 });
int sum = Convert.ToInt32(res);
Aprovechando para hacer publicidad ^_^ con librería LateBindingHelper se simplificaría a
IOperationInvoker calc = BindingFactory.CreateObjectBinding(GetCalculator());
int sum = calc.Method("Add").AddParameter(10).AddParameter(20).Invoke<int>();
Sin embargo utilizando la palabra clave dynamic, todo quedaría:
dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);
Sin embargo, si cogemos este pequeño código vemos que se están realizando muchas cosas. En la primera línea, la palabra dynamic define una variable con un tipado fuerte. Es decir, una variable declarada con dinamic es de tipo dinámico, que viene a ser una especie de object al poder asignarle cualquier valor. Sin embargo no podemos hacer castings implícitos:
dynamic x = 1;
int i = x; //NO!

En la segunda estamos invocando el método Add de forma dinámica, esto es: el enlace al método se realiza en tiempo de ejecución, y esto ocurre para cualquier operación en la que esté involucrada una variable dynamic. Además, el resultado de la operación se convierte también de forma dinámica al tipo que asignamos, int en este caso.

Es indudable que la nueva keyword dynamic muy útil en muchos casos. Aunque en algunos sitios ya ponen el grito en el cielo diciendo que podría sobreutilizarse y derivar en diseños pobres. Yo digo que 'como todo'. Prácticamente cualquier cosa de un lenguaje la puedes utilizar de una manera incorrecta manera y acabar creando un diseño pobre.

domingo 16 de noviembre de 2008

Fluent Interfaces

2 Comentarios

Navegando un poco e investigando sobre ideas y tecnología a usar en mi PFC llegué a uno de los muchos artículos de Martin Fowler donde se discute una forma de generar una API, que aunque ya conocía en esencia, no sabía que tenía un nombre: Fluent Interfaces

Básicamente cuando se crea un "interface fluido" se pretende que las operaciones que puedes realizar en un objeto dependan del contexto generado por la operación previa. Con un ejemplo es mucho más sencillo, y corto y pego el que aparece en la wikipedia:
GlutApp app(argc, argv);
app.setDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); // Set framebuffer params
app.setWindowSize(500, 500); // Set window params
app.setWindowPosition(200, 200);
app.setTitle("My OpenGL/GLUT App");
app.create();
El código de arriba crea una ventana de visualización OpenGL usando GLUT. Bien, pues el mismo código utilizando el tipo de interface que discutimos sería:
FluentGlutApp app(argc, argv)
.withDouble().withRGBA().withAlpha().withDepth()
.at(200, 200).across(500, 500)
.named("My OpenGL/GLUT App");
app.create();

Personalmente creo que el último ejemplo expresa de forma más clara las operaciones a realizar (inicialización), y sobre todo a qué objeto están dirigidas. Al respecto, y como curiosidad, si habéis utilizado Rhino Mocks como librería de Mocks para vuestros casos de prueba, comprobaréis que a partir de la versión 3.2 añade una sintaxis fluida


Dándole vueltas al asunto, me di cuenta que tenía un proyecto previo que podría beneficiarse -y mucho- de este tipo de interfaces: mi librería para simplificar llamadas utilizando Late Binding en C#, de la que hablé en una entrada anterior. Así que dejando aparcado el PFC un tiempo y dedicándole un par de días, he creado la versión 2.0 2.1 de la librería, cuyo código está subido ya al repositorio.


Una de las mejoras que provoca el nuevo interface es la llamada de métodos con múltiples parámetros. Por ejemplo, supongamos una operación que recibe 2 parámetros, uno de ellos por referencia:

void MyClass.MyOperation(string command, ref int result);
Con la versión 1.* tendríamos que hacer :

ILateBindingFacade lb = LateBindingFactory.CreateObject(typeof(MyClass) );
string cmd = "myCommand";
int result;
object[] args = Args.Build(cmd, result);
lb.Call("MyOperation", Args.ByRefIndexs(1), args);
result = args[1];
Con la nueva versión sin embargo el código queda así:
IInvoker invoker = LateBindingFactory.CreateObject(typeof(MyClass) );
object result;
invoker.Method("MyOperation")
.AddArgument("myCommand")
.AddRefArgument(result)
.Invoke();

result = invoker.LastCallArguments[1];

Vale que es "mi" librería y que los niños de uno nunca son feos, pero a mi me parece muchísimo más elegante y simple que la primera versión, y no sólo se queda en elegancia, sino que las operaciones permitidas dependen del contexto como había hablado anteriormente. Por ejemplo, si en un objeto IInvoker llamamos a la operación Method para especificar el nombre de la operación a invocar sobre él, los únicas operaciones que pueden ejecutarse son AddParameter, AddRefParameter e Invoke, que son las únicas que tienen sentido.


La legibilidad aumenta según más complejo es el uso. Esto se puede ver si retomamos el código para controlar Microsoft Word que tenía en la versión 1.3 de la librería, y lo comparamos con la nueva versión:

ILateBindingFacade word = LateBindingFactory.CrateAutomationBinding("Word.Application");

//Get Word object
ILateBindingFacade wordDoc = word.Get<ILateBindingFacade>("Documents").Call<ILateBindingFacade>("Add");
ILateBindingFacade selection = word.Get<ILateBindingFacade>("Selection");

string str = "Hello World!";

word.Set("Visible", true);

selection.Call("BoldRun");

foreach (char c in str)
{
selection.Call("TypeText", Args.Build(c.ToString()));
System.Threading.Thread.Sleep(200);
}

word.Call("Quit", Args.Build(0) );

Y la nueva versión 2.1 que lava más blanco:
IInvoker wordApp = BindingFactory.CreateAutomationBinding("Word.Application");
//Get Word object
IInvoker document = wordApp.Property("Documents").Get<IIinvoker>();

document
.Method("Add")
.Invoke();

IInvoker selection = wordApp
.Property("Selection")
.Get<IInvoker>();

string str = "Hello World!";

//Make workd visible
wordApp
.Property("Visible")
.Set(true);

//Activate bold
selection
.Method("BoldRun")
.Invoke();


foreach (char c in str)
{
selection
.Method("TypeText")
.AddParameter(c.ToString())
.Invoke();

System.Threading.Thread.Sleep(200);
}

//Quit
wordApp
.Method("Quit")
.AddParameter(0)
.Invoke();
Sólo queda hacer la pregunta de rigor, ¿qué interface creeis que sería más cómodo de utilizar?