Hace más de un año, escribí:
Algoritmos Genéticos con AjAgents y Concurrency and Coordination Runtime (CCR)
Genetic Algorithms with AjAgents and Concurrency and Coordination Runtime (CCR)
explorando la concurrencia en la implementación de una solución al Travelling Salesman Problem usando un algoritmo genético. Al final del año pasado, escribí un ejemplo, usando lo que ya había implementado en C#:
GoRoutines y Canales en C#
GoRoutines and Channels in C#
El código de este experimento está en el proyecto AjConcurr.Tsp en el repositorio:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjConcurr
Este es el resultado (no es una gran interfaz… ;-):
TSP no es un buen problema para resolver usando algoritmos genéticos: el resultado depende de la mejora de la población de soluciones tentativas después de cada iteración. En esta implementación uso un algoritmo de mutación (no estoy usando crossover) que intenta generar mejores individuos (en general, un algoritmo de mutación debería solamente cambiar al individuo, dejando la emergencia de mejoras al algoritmo genético mismo). El código está escrito en un gran método, que se lanza desde el botón Run. Es código sólo para demostración de la idea.
Al comienzo de ese método, se definen los canales a usar:
Channel genomas = new Channel();
Channel evaluated = new Channel();
Channel bestsofar = new Channel();
Channel mutator = new Channel();
Recordemos: podemos poner un valor (un objeto cualquiera) en un channel, y en otro thread, podemos obtener el valor. Si colocamos un valor en un channel sin que haya otro thread que está esperando ese valor, nuestro thread se bloquea. Si tratamos de leer el próximo valor de un channel, y no hay tal valor, nuestro thread se bloquea. De esta forma, productores y consumidores de valores se van sincronizando.
El canal Evaluated recibe individuos (soluciones del problema) y los collecciona, formando una nueva población. El mejor individuo es enviado al canal bestsofar, que mantiene cuál es el mejor individuo hallado hasta el momento en el proceso. El canal genomas recibe un individuo, evalúa la solución que representa, y lo reenvía al canal evaluated. El canal mutator recibe un genoma, lo muta y evalúa. El nuevo genoma es entonces reenvíado al canal evaluated.
De esta forma, varias poblaciones son procesadas en paralelo. Para ser más exacto, no hay el concepto de población, sino que hay N * M individuos en proceso. Por cada N de esos individuos que llegan al final del proceso, los mejores son seleccionados, y un nuevo grupo de N individuos es inyectado de nuevo en el proceso.
Veamos el código, compuesto de varias GoRoutinas. Primero, lanzamos una goruotine para generar la poblaciones iniciales:
// Generates the initial populations
GoRoutines.Go(() =>
{
Genoma genoma = GenerateGenoma();
for (int j = 0; j < PopulationSize * 10; j++)
{
genomas.Send(this.Mutate(genoma));
}
});
Se lanza una goroutine para evaluar nuevos genomas:
// Evaluate genomas
GoRoutines.Go(() =>
{
while (true)
{
Genoma genoma = (Genoma)genomas.Receive();
genoma.value = this.CalculateValue(genoma);
evaluated.Send(genoma);
}
});
Una goroutine para coleccionar genomas, seleccionarlos, y generar un nuevo grupo, población:
// Collect and select
GoRoutines.Go(() =>
{
GenomaComparer comparer = new GenomaComparer();
while (true)
{
List<Genoma> genomalist = new List<Genoma>();
for (int k = 0; k < PopulationSize; k++)
{
Genoma genoma = (Genoma)evaluated.Receive();
genomalist.Add(genoma);
}
genomalist.Sort(comparer);
GoRoutines.Go(() => bestsofar.Send(genomalist[0]));
for (int k = 0; k < PopulationSize / 5; k++)
GoRoutines.Go(() => evaluated.Send(genomalist[k]));
for (int k = 0; k < PopulationSize / 5; k++)
{
GoRoutines.Go(() => mutator.Send(genomalist[k]));
GoRoutines.Go(() => mutator.Send(genomalist[k]));
GoRoutines.Go(() => mutator.Send(genomalist[k]));
GoRoutines.Go(() => mutator.Send(genomalist[k]));
}
}
});
A goroutine for receives genomas, improve them, and forwards them to evaluated channel:
// Mutates
GoRoutines.Go(() =>
{
Random rnd = new Random();
while (true)
{
Genoma genoma = (Genoma)mutator.Receive();
Genoma newgenoma = this.Mutate(genoma);
while (newgenoma.value >= genoma.value)
{
if (rnd.Next(3) == 0)
break;
newgenoma = this.Mutate(genoma);
}
evaluated.Send(newgenoma);
}
});
Una goroutine para recibir y procesar los mejores encontrados hasta el momento:
// Receives and draws the results
GoRoutines.Go(() =>
{
Genoma best = null;
while (true)
{
Genoma genoma = (Genoma)bestsofar.Receive();
if (best == null || best.value > genoma.value)
{
best = genoma;
this.BestGenoma(genoma);
}
}
});
No puse una operación de crossover. El caso es que el problema y el algoritmo podrían cambiar, pero la idea es explorar la implementación paralela de un algoritmo genético.
El principal problema que encontré es en el proceso de una población acumulada. En ese paso, la goroutine tiene que enviar varios valores a varios canales. Si alimenta un canal, y ésto la bloquea, los demás canales no recibirán sus valores. Tuve que lanzar varias goroutines para solucionar este problema, cada una alimenta a un canal.
Próximos pasos: implementar el algoritmo usando Queue Channels como en:
Queue Channels en AjSharp
e implementar un actor model, colocando cada paso en un agente, no en una goroutine. Llegados a ese punto, podría intentar poner los agentes en distintas máquinas.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Una de las características que quería tener desde el principio en mi intérprete AjSharp era que las funciones y rutinas sean ciudadanos de primera clase en el lenguaje (en este post uso la palabra clave function pero se puede usar también sub para definir una subrutina).
El código está en el proyecto de código de Google:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
AjLanguage es el lenguaje núcleo (que no tiene ni parser ni lexer, sólo lo esencial del intérprete). AjSharp es el parser y el lexer implementado sobre el AjLanguage. De esta forma, podría implementar AjBasic u otros lenguajes similares, usando AjLanguage como base y núcleo.
En AjSharp, podemos definir funciones como en otros lenguajes:
function Square(n) { return n*n; }
Podemos definir una función anónimca y asignarla a una variable:
MySquare = function (n) { return n*n; };
Un functional value puede ser usado como parámetro:
function Apply(func,values)
{
list = new List();
foreach (value in values)
list.Add(func(value));
return list;
}
numbers = new List();
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
function Square(n) { return n*n; }
squared = Apply(Square, numbers);
squared2 = Apply(function (n) { return n*n; }, numbers);
La función Apply definida arriba, toma una función, una lista, y retorna otra lista de elementos que resultan de evaluar f(e), donde f es la función recibida, aplicada a cada elemento e de la lista que se pasó como parámetro. Podemos invocar Apply con el nombre de la función (que en realidad es el nombre de la variable que contiene al functional value) o definidiendo la función directamente en la invocación.
Estoy pensando sobre algunas alternativas de implementación del alcance y visibilidad de variables en expresiones funcionales (dará para otro post el tema). Si definimos una función, AjLanguaje (el núcleo de AjSharp) usa una closure (clausura), así que cuando la función es invocada, el binding environment original es usado. En los ejemplos de más abajo se usa esa característica.
Cada función implementa:
public interface ICallable
{
int Arity { get; }
IBindingEnvironment Environment { get; }
object Invoke(IBindingEnvironment environment, object[] arguments);
object Invoke(object[] arguments);
}
esto es, cuando es invocada, recibe un binding environment (un diccionario que dado el nombre de una variable nos da su valor asociado). Un funcional value no usa ese binding environment recibido, y lo reemplaza por el environment original que tenía en el momento de haber sido definida, creada.
Podemos retornar una función como retorno de una función:
function MakeIncrement(x)
{
return function(n) { return n + x; }; // x is bounded to local parameter
}
Increment2 = MakeIncrement(2);
result = Increment2(2);
// result == 4
Increment3 = MakeIncrement(3);
result2 = Increment3(2);
// result2 == 5
result3 = MakeIncrement(4)(3);
// result3 == 7
x = 4;
result4 = function(n) { return n+x; }(5);
// result4 == 9
MakeIncrement retorna una función que usa y accede a la variable x, ligada en el binding environment de llamada.
En el último comando, la función es definida e invocada en el mismo comando.
AjSharp tiene clases, y podemos definir funciones y rutinas como métodos:
class Person
{
var Name;
var Age;
function AddYears(years)
{
this.Age = this.Age + years;
}
}
Pero también podemos agregar funciones en cualquier momento:
class Person
{
var Name;
var Age;
function Person()
{
x = 100;
this.GetYears = function () { return this.Age; };
this.GetX = function() { return x; };
}
}
adam = new Person() { Name = "Adam", Age = 800 };
result = adam.GetYears();
// result == 800
result2 = adam.GetX();
// result2 == 100
o agregar funciones/rutinas directamente a objetos:
adam.AddYears = sub(n) { this.Age = this.Age + n; };
Hace unas semanas, leí el post:
Functional Programming in Javascript
de James Carr, así que decidí probar las capacidades de AjSharp para manejar functional values adaptando algunos de esos ejemplos, como:
function runningSum(start){
sum = start; // you could use var sum, it's local
return function(a){
sum = sum + a; // function access the "outer" sum
return sum;
};
}
sum = runningSum(3); // makes function
result = sum(2); // returns 5
result2 = sum(10); // returns 15
Podemos usar directamente el parámetro como acumulador:
function runningSum(start){
return function(a){
start = start + a; // function access the "outer" start
return start;
};
}
sum = runningSum(3); // makes function
result = sum(2); // returns 5
result2 = sum(10); // returns 15
Agregué soporto para aplicar una función como si fuera un método a un objeto, como en uno de los ejemplos de Carr (este tipo de llamada es soportada en Javascript que tiene la clase Function):
person = new { Name = "Adam", Age = 800 };
GetAdjustedAge = function (x) { return x + this.Age; };
result = GetAdjustedAge.Call(person, 10); // result == 810
Próximos pasos: estabilizar el alcance de las varaibles y el acceso al environment global. Por ahora, me divertí bastante implementando valores funcionales
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Hace una semanas, implementé channels en mi intérprete AjSharp. Pueden leer sobre el tema en:
Channels and GoRoutines in AjSharp (part 1)
Channels and GoRoutines in AjSharp (part 2)
Canales y GoRoutines en AjSharp (Part 1)
Canales y GoRoutines en AjSharp (Part 2)
Si uno lee el valor de un canal, y no hay valor disponible, el thread que está tratando de leer se bloquea hasta que el valor esté disponible. Esta es una manera de sincronizar consumidores y productores que operan sobre el canal. Pero algunas veces queremos poner un valor en un canal y continua, sin bloqueo. Para eso, agregué una “queue channel” (podría llamarla queued channel): pueden poner N valores en el canal sin bloqueo, porque se van guardando en una cola interna. El tamaño de la cola se determina en un parámetro en el constructor. La cola está limitada en tamaño para poder preservar sus capacidades de sincronizació.
El nombre de la nueva clase es QueueChannel. Un test:
[TestMethod]
public void CreateAndUseQueueChannelWithTenElements()
{
QueueChannel channel = new QueueChannel(10);
for (int k = 1; k <= 10; k++)
channel.Send(k);
for (int k = 1; k <= 10; k++)
Assert.AreEqual(k, channel.Receive());
}
El canal recibe 10 valores sin bloquearse. Si enviamos más valores, debemos usar otro thread:
[TestMethod]
public void CreateAndUseQueueChannelWithMoreEntriesThanSize()
{
QueueChannel channel = new QueueChannel(10);
Thread thread = new Thread(new ThreadStart(delegate()
{
for (int k = 1; k <= 20; k++)
channel.Send(k);
}));
thread.Start();
for (int k = 1; k <= 20; k++)
Assert.AreEqual(k, channel.Receive());
}
Para probarlo en un ejemplo, reescribí el ejemplo de números primos usando queue channels:
numbers = new QueueChannel(10);
running = true;
k = 1;
go while(running) { k++; numbers <- k; }
function filter(in, out, prime)
{
while (true)
{
value = <-in;
if (value % prime)
out <- value;
}
}
function makefilter(channel, number)
{
newchannel = new QueueChannel(10);
go filter(channel, newchannel, number);
return newchannel;
}
channel = numbers;
number = <-channel;
while (number < 1000)
{
PrintLine("Prime " + number);
channel = makefilter(channel, number);
number = <-channel;
}
running = false;
El código está en mi Google code project:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
Mi motivación para un queue channel apareció en ejemplo de algoritmo genético en paralelo, que sufría de demasiados bloqueos. Veo que un buen algoritmo no necesitaría colas, pero si uno tiene muchos consumidores y productores, y un thread produce valores para ser colocados en más de un canal, podría pasar que al colocar el valor en el canal A, no lleguemos a entregar el valor en el canal B, por bloqueo en la primera operación. Una forma de evitar el bloqueo es usando el comando go:
go channel <- newvalue;
Pero me parece que es demasiado lanzar una goroutine solamente para alimentar un canal.
Tendría que presentar el algoritmo genético en paralelo (que ya hace unos meses presenté), y escribir algunas ideas para implementar un actor model en AjSharp, y usar agentes distribuidos.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Había implementado canales en mi intérprete AjSharp. Describí el trabajo y ejemplos en:
Channels and GoRoutines in AjSharp (part 1)
Channels and GoRoutines in AjSharp (part 2)
Canales y GoRoutines en AjSharp (Part 1)
Canales y GoRoutines en AjSharp (Part 2)
También los usé directamente en C#:
GoRoutines y Canales en C#
Podría usar canales para implementar futures. Una opción es usar Task Parallel Library de Microsoft, pero entonces debería usar .NET 4: aparentemente, la librería original no está más disponible para .NET 3.x. Imagino que podría aprovecharme de las nuevas clases del nuevo framework, pero mientras, quiero explorar las ideas de programación con futures por mi cuenta.
Los canales pueden ser usados para implementar el proceso de futures:
channel = new Channel();
go channel <- SomeLengthlyCalculation();
// .. more process
result = <- channel;
Pero los canales han sido diseñados para resolver otros problemas: para tener varios valores durante su vida, y ser usados como objetos de sincronización entre productores y consumidores de esos valores. La variable channel del ejemplo de arriba, si se usara como un future, podría ser consultada sólo una vez. Es un pequeño problema, pero señala una diferencia entre canales y futures.
Así, escribí una nueva clase nativa, accesible desde programas AjSharp, llamada Future:
public class Future : IReference
{
private object value;
private ManualResetEvent handle = new ManualResetEvent(false);
private bool set = false;
public void SetValue(object value)
{
lock (this)
{
if (this.set)
throw new InvalidOperationException("Future value already calculated");
set = true;
this.value = value;
this.handle.Set();
}
}
public object GetValue()
{
this.handle.WaitOne();
return this.value;
}
}
En la versión actual en el trunk, la interface IReference (que contiene méteodos SetValue, GetValue) está implementada por la clase Channel. Los operadores AjSharp a <- b, a = <-b, son mapeados a esos métodos. Entonces podemos usar Futures de esta forma:
future = new Future();
go future <- SomeLenghtlyProcess();
// ... more process
result = <-future;
result2 = <-future;
La consulta del valor del future es explícita, usando el operador <-. De esta forma, podemos pasar la variable future a cualquier otra función, usando el nombre, sin ver todavía su valor. Podría eliminar el go en el seteo de la variable future, asumiendo que el valor a la derecha de ese valor se calcula en paralelo. Pero prefiero hacer que el paralelismo quede explícito, usando el comando go.
Esta es una mejora menor en el procesamiento en paralelo de AjSharp. Estoy trabajando en queue channels: channels que soporten el manejo de más de un valor, sin bloquearse. Pueden ver el código y ejemplos en:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
Quiero implementar agentes, para soportar la programación basada en el actor model. Podría ser interesante tener a la palabra agent como palabra clave, como la palabra class. Y que esos agentes puedan ser ejecutados en máquinas remotas.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Es bueno enterarse que hay más NerdDinners en mi país Argentina. Ahora, mañana miércoles, viene una más, en Tandil, provincia de Buenos Aires. Más información en:
http://www.nerddinner.com/1606
Me informa el bueno de @cwalzer que no es la primera, debe ser como la quinta que organizan por ahí. Ya tuvimos dos en Buenos Aires, leer:
Segunda Nerddinner en Buenos Aires
Tandil tiene un actividad de desarrollo importante, movilizada por la presencia de empresas y, en particular, por el motor que es la Universidad del Centro. Ya estuve dos veces en la ciudad, dando alguna charla:
ASP.NET y Ajax en Tandil, Buenos Aires
Generación de Código y AjGenesis en Tandil
@cwalzer hace un tiempo que se mudó a Tandil, donde se vive una vida distinta de la que se ve en Buenos Aires. Me cuenta que hay una gran actividad inmobiliaria, lo que revela que Tandil está creciendo como centro concentrador de actividades.
Tiene evidencia de anteriores reuniones en Tandil en:
http://twitpic.com/j0jg2
http://twitpic.com/fehpq
Jeje… parece que le dan al asado por ahí, algo más difícil de encontrar por aquí (por lo menos a un precio que no sea de turista).
Conocen otras Nerddinners? Hay alguna por su zona?
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Gracias a la comunidad ALT.NET Hispano, este sábado 23 de enero, a las 18hs GMT (acá en Buenos Aires, Argentina, 15hs). El tema es IronRuby y será presentado por Mario Chavez.
Mario presentó el temario en la lista de ALT.NET Hispano, así:
Que es lo que pueden esperar de esta VAN?
- Lenguaje Ruby
+ Que es
- Ironruby
+ Que es
+ Variables
+ Metodos
+ Clases
+ Sintaxis sugar
+ Expresiones regulares
- Aplicaciones de Ruby con Ironruby
+ BDD con Rspec
+ BDD con Cucumber
+ Sinatra DSL
+ Rails
- Integracion con .NET
+ BDD de codigo C#
+ Hosting de Ruby en C#
+ ASP.NET MVC con Ruby
+ Silverlight con Ruby
Pueden leer el post de Mario Chavez sobre sus actividades de este mes con IronRuby: Webcasts sobre IronRuby en Enero.
Si no conocen IronRuby, comiencen desde:
http://www.codeplex.com/IronPython
Mis enlaces sobre el tema:
http://delicious.com/ajlopez/ironruby
http://delicious.com/ajlopez/ruby
http://delicious.com/ajlopez/dlr
(yo tendría que comenzar a jugar con Dinamic Language Runtime, DLR, o hacerme uno … :-)
Vean también Sinatra:
http://www.sinatrarb.com/
(sí, sí, hay AjRuby, pero todavía en inception… ;-) .. leer TDD and Code Kata: Writing a lexer for AjRuby)
Más información sobre cómo asistir a la reunión virtual, software necesario, enlaces: http://altnet-hispano.pbworks.com/Descripcion-de-Reuniones
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Hace un tiempo, seguramente via un mensaje en Twitter, me encontré con el post Agile/Pervagile on Slashdot en el blog multi-autor de Agile Advice – Working With Agile Methods (Scrum, OpenAgile, Lean). Es una lectura interesante. El autor presenta una descripción de lo que llama Pervagile, the Perverted Agile, lo ágil pervertido por malas prácticas. Muchas compañías dicen que están usando Agile, con Scrum o algo parecido, pero, en realidad, su adopción es parcial. El autor menciona otras desviaciones, que tienen nombres como: Scrumbutt, Waterscrum, Scrummerfall, pero esos temas merence un post aparte.
Pero hoy, quiero comentar sobre el párrafo (disculpen que no traduzca):
Agile is Hard
Okay, I’m actually being a little dis-honest. The real truth is that doing agile is extremely, exceptionally, agonizingly difficult (for most people in most organizations). Why? Because agile is not just another process to roll out. It is, as has been mentioned in numerous places, a deep cultural change. Agile is actually a liberation movement for people involved in software development. Like most movements, however, it has been subject to corruptive forces.
Bien, cierto, pero… Quiero moderar las afirmaciones de arriba, esta vez en el contexto de la aplicación de Scrum (no de Agil en general). Si estamos involucrados en la introducción de Scrum por primera vez en su compañía, claro que va a necesitar un cambio cultural, pero veo que no tendría que ser profundo (“deep” como dice arriba). Lo que necesitamos es un grupo de gente proactiva que componga el primer equipo, un buen ScrumMaster que maneje la introducción de Scrum, y un Product Owner que conozca su función. Y (y no es un Y pequeño) el soporte de parte de la gerencia, para protejer al equipo de intereferencias externas, Y ESPECIALMENTE el soporte al Product Owner: protejerlo/a de las presiones de otros genertes, fuerzas políticas, que podrían alterar las prioridades en el Product Backlog, o que pudieran intereferir en el medio del Sprint. El resto de la compañía pueden seguir trabajando a la vieja usanza. Pero necesitamos que nuestro primer proyecto sea protejido. Necesita protección y cuidado. No un gran cambio cultural. El cambio en el resto de la compañía podría ser progresivo. Y el cambio en el equipo (pensando en que es su primer encuentro como miembros de un Scrum) sería progresivo, ayudado por un ScrumMaster experimentado.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Gracias a la comunidad de ALT.NET Hispano, he podido presentar el tema Domain-Driven Design, como anunciara hace un tiempo en:
ALT.NET Hispano VAN (Reunión Virtual): Domain-Driven Design
Y ahora, la comunidad se ha tomado el trabajo de escribir los principales puntos, y editar y publicar el video de la presentación y la charla. Pueden verlo en:
ALT.NET Hispano VAN 2009-12-19 Domain-Driven Design
En la presentación, comenté los principales puntos (DDD daría para varias horas), pero no fue sólo slides, sino que comentamos el código del proyecto: http://code.google.com/p/ndddsample
Como decía, DDD es un gran tema, que seguramente dará para más presentaciones en detalle, y más ejemplos de implementación. Por un lado, el uso de objetos y dominio estuvo desde temprano presente en el ambiente Java, pero por razones históricas, al llegar .NET no se hizo hincapié en el tema de armar un dominio, sino que se puso énfasis en los datos, más o menos directos.
Igualmente, el problema no es armar un dominio con objetos, sino llevarlo a una implementación exitosa, donde el dominio pueda convivir con tecnologías de todos los días, como bases de datos relacionales, concurrencia de usuarios, interfaces de presentación de distinto tipo, etc…. Por ejemplo, uno de los ejemplos de uso de dominio transparente, ha sido dado desde hace años, en el ambiente de la programación con Smalltalk. Pero hay que reconocer que esta tecnología no ha conseguido llegar al “mainstream” de desarrollo, y, en mi opinión, también se ha aislado de otros activos, como el acceso a base de datos y aplicaciones distribuidas.
Es aquí donde las ideas de Eric Evans y otros, abrieron la puerta para que hoy tengamos algunos patrones a adoptar o adaptar, para construir un dominio, e implementarlo de forma razonable, con lenguajes y tecnologías “mainstream”. Aún necesitamos varios artefactos para conseguirlo (me gustaría que fuera más simple, como en Smalltalk (no me olvido de mi post ¿Es tiempo de volver a la simplicidad?)), pero para un programador pragmático, las ideas de DDD tienen aplicación práctica.
Para mí, fue una feliz experiencia haber podido participar de este evento. Agradezco a todos lo que hicieron posible esto. Es notable lo que ha dado Internet y la Web en particular, para que podamos entre todos, compartir información, conocimiento, discutir opiniones, y aprender de cada uno algo. Y que las actividades como éstas, vayan quedando con “entregable”: no sólo una reunión, sino algo como resultado tangible.
Espero ir completando la página de la VAN con algunos enlaces más, pero veo que la gente de ALT.NET Hispano ha hecho un gran trabajo. Muchas gracias!
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
El bueno de Tobias Mayer visitará Buenos Aires en este mes de Enero. Fue Tobias mi instructor en el curso de Certified Scrum Master que tomé en el 2006. Tobias es conocido en todo el mundo ágil, habiendo trabajado en grupos de desarrollo usando Scrum, y dando cursos en varios países. Pueden ver un día de su trabajo como Scrum Master en:
Tales from the Scrum: un día en el equipo
Leo en el blog de Juan Gabardini el detalle de los cursos que dará Tobias:
Tobias Mayer ya ha venido varias veces a Argentina, y dio 6 cursos de CSM. Alan Cyment es el único CST hispanoparlante, y dio muchos cursos en Argentina. En enero Tobias nos visita en la semana del 25 al 26 y aprovechamos con varios actividades:
Enero 25: Scaling Scrum (gratuito) (x Tobias)
Enero 26: Consultas CSP (gratuito) (Alan y Tobias)
Enero 27: The Spirit of Scrum - (pago *) (x Tobias)
Enero 28: Improvisation for agile teams - (pago *) (facilitado por Alan Cyment, co-facilitado por Tobias)
(*) En ambos casos, cada día cuesta 220 usd + IVA. Tomando ambos cursos el costo es 330 usd + IVA - La facturación la realizará Agilar, que organiza estos eventos.
NOTA: inicialmente planificamos un CSM los días 25 y 26. Por el interés en los talleres avanzados, finalmente suspendimos ese CSM. Para los interesados en CSM, en marzo sr hará uno (Alan)
CSM se refiere a Certified Scrum Master. CSP es Certified Scrum Practicioner. Como se lee en http://www.agiles.org/agiles-bsas:
Luego de hacer un curso oficial de Scrum te convertis en CSM. Pero, más allá del valor de tomar el curso, todos sabemos que no es una "certificación" muy exigente. Por eso puede interesarte dar el próximo paso en las certificaciones de la Scrum Alliance: Certified Scrum Practicioner (http://www.scrumalliance.org/pages/certified_scrum_practitioner).
Les aconsejo que participen de alguno de estos cursos. Tener a Tobias es tener acceso a alguien que ha trabajado intensamente con Scrum, en distintas situaciones y proyectos.
Ya había traducido dos escritos de Tobias:
Tales from the Scrum: El corazón de Scrum
Tales from the Scrum: La esencia de Scrum
Para conocer más sobre Tobias visiten su blog Agile Thinking, y su iniciativa WelfareSCM (entramiento Scrum de bajo costo), también el video que grabó sobre el tema en InfoQ.
Y pueden seguirlo en Twitter como @tobiasmayer.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Gracias a todos los que comentaron mi anterior post:
Hacia una Historia Clínica Digital de código abierto
Ha sido uno de los post que más comentarios ha recibido últimamente. Y me ha servido de mucho la información y los enlaces que me han pasado.
Desde entonces, he seguido investigando las ofertas de código abierto (y algunas comerciales) de sistemas relacionados con salud. Como comentaba ahí, una forma de que se adopte un sistema nuevo de código abierto, es que comience ha ser utilizado en algún caso real. En este tema, el target a alcanzar, en primer lugar, son clínicas, pequeñas instituciones, que no tengan el presupuesto para alcanzar a comprar un sistema comercial más caro. También ese tipo de target, sirve para ir probando en escala mediana el funcionamiento de un sistema.
Pero no quisiera que la primera implementación fuera para una institución en particular. El hacer un software sin cliente final, es un problema. Pero también es hacerlo para un cliente particular, porque es difícil armar el sistema manteniendo una claridad y flexibilidad que perdure, y que no se vea percutido por decisiones de diseño para satisfacer las necesidades inmediatas de un cliente. Es posible, pero he visto pocos casos de éxito siguiendo ese camino. Se puede hacer un núcleo, una prueba de concepto, sin tener un cliente final, y luego, una vez armado ese núcleo, simplísimo, ver de extenderlo, modificarlo (en poco o en mucho), ponerlo a prueba, cuando se llegue a un cliente real. En este caso, en este proyecto, preferiría un Fresh Start, y por un tiempo, basarse en lo que ya está publicado en otros sistemas.
Uno de los enlaces que mencionaba en mi anterior post era:
List of Open Source Healthcare Software
Ahí encontré la denominación
Hospital Information System
He ido coleccionado enlaces sobre salud (y sistemas de salud) en:
http://delicious.com/ajlopez/health
http://delicious.com/ajlopez/medicine
Ahí guardé varios enlaces a sistemas HIS (Hospital Information System) de código abierto, y dos o tres comerciales, así como ha sitios de información sobre medicamentos, drogas, contraindicaciones, etc... Una lista interesante es:
Hospital Information Systems (HIS): Commercial, free and open source list
como para tener una lista inicial referentes de la oferta actual. Por ahora, estuve viendo el gran trabajo de la gente de:
http://www.care2x.org/
Care2x integrates data, functions and workflows in a healthcare environment. It is currently composed of four major components. Each of these components can also function individually.
- HIS - Hospital/Healthservice Information System
- PM - Practice (GP) management
- CDS - Central Data Server
- HXP - Health Xchange Protocol
Our Mission
We strive to develop the most useful and practical integrated healthcare information system which is open for others to develop further.
The "Team"
The development team has currently more than 100 members with different skills and backgrounds. They come from more than 20 nations.
Me parece interesante el desarrollo de un HIS, porque:
- No es trivial
- Tiene desafíos de extensibilidad
- Hay un mercado vertical ya existente
- Hay referentes, contra los cuales medirse
- Gran parte se podría ofrecer como SaaS (Software as a Service)
Por supuesto, un HIS completo es un trabajo de mucha gente, como demuestra el Care2x de arriba. Pero creo que puedo escribir por lo menos un núcleo, que considere las entidades más importantes, como pacientes, profesionales médicos, entidades financiadoras (Obras sociales, medicina prepaga), drogas y medicamentos, turnos, tratamiento ambulatorio, no sé si internación.
Las tecnologías candidatas que podría usar para encarar una prueba de concepto, son: WinForm, ASP.NET MVC, C#, SQL Server (he visto mucho hecho en Java, PHP y otras, pero no he encontrado tanto en .NET open source, igual tengo que seguir revisando enlaces). Me gustaría aplicar, por un lado, generación de código, y por otro, TDD, si fuera posible. Dejar abierto, en cuanto fuera posible, el consumo del sistema desde otras tecnologías Front End. El dominio en POCO (Plain Old CLR Objects), con lo que no usaría LINQ2Sql o Entity Framework en principio (sé que hay soporte para POCO en EF, pero es un Work In Progress, no quisiera sumar más complejidad a algo que ya es complejo). Podría usar un ORM, pero quiero explorar el camino de ORM por código propio: escribir el mapeo generando código automático para las entidades que vayan surgiendo, con manejo de Lazy Load, y Cache… Pero son ideas técnicas. Lo importante es comenzar con algo, que vaya tomando forma.
Primer candidato a sistema a implementar: un sistema de turnos.
Nos leemos!
Angel "Java" Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
http://www.msmvps.com/lopez
http://ajlopez.wordpress.com
http://ajlopez.zoomblog.com
Pueden encontrar el código de un intérprete que sigue una sintaxis similar a Clipper (hace un tiempo escribí Remember Clipper (en Anglish, Angel's English) Recordando a Clipper ahí hay más enlaces sobre el lenguaje). Es de código abierto, y está albergado en mi proyecto de Code Katas en Goggle code:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjClipper
Es un intérprete, escrito en C#, y como otros que escribo, tiene acceso a los objetos nativos de .NET. Pienso que todo intérprete debería aprovechar la librería de clases que tenga el framework que lo soporta (que puede ser .NET o Java). Escribí el intérprete para:
- Practicar TDD (Test-Driven Development)
- Crear una herramienta simple para apalancar el conocimiento de Clipper de uno de los equipos en los que participo, para que practiquen algo más de .NET
Debería escribir posts sobre la implementación interna, visibilidad de variables, comandos, acceso a objetos .NET, manejo de work areas, etc… Una idea que estoy explorando es ejecutar el intérprete desde una páginas ASP.NET:
(Hay una aplicación web en el proyecto con una única página de prueba para ejecutar un texto que se ingresa).
Mi colección de enlaces sobre Clipper:
http://delicious.com/ajlopez/clipper
Debería mejorar el acceso a base de datos, y completar el lenguaje con más comandos y características.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://www.msmvps.com/lopez
http://ajlopez.wordpress.com
http://ajlopez.zoomblog.com
http://twitter.com/ajlopez
El próximo sábado 16 de Enero, tendremos nueva VAN a las 18 GMT, 3 de la tarda acá en Buenos Aires, organizada por la comunidad ALT.NET Hispano. El presentador del tema, Diego Jancic, explica:
Para los interesados, les comento que la idea es hablar en torno a FitNesse y Acceptance Testing, y que la conversacion guie los detalles y los intereses, para profundizar sobre cualquier tema; basicamente es mantener el espiritu Agile de reuniones.
De cualquier forma, estoy preparando todo como para que haya una base de temas interesante para guiar la VAN.
Resumen:
La herramienta FitNesse, incluyendo todo lo necesario para comenzar y llegar a integrarla con .NET. Adicionalmente, se incluiran otros 2 temas que son claves para aprovechar FitNesse al maximo. Primero, será indispensable hablar de Acceptance Testing, para entender su importancia y utilidad en el desarrollo de software; y luego, Selenium, una herramienta que permite probar aplicaciones web. Ésta ultima será simplemente un ejemplo de aplicacion, ya que FitNesse no esta limitado a aplicaciones Web, cualquier tipo de extension y aplicacion esta permitida.
Objetivo:
Permtir a cualquier persona con conocimientos de programacion, configurar un ambiente de pruebas automatizadas y orientadas al cliente. De forma que alguien sin conocimientos de sistemas, pueda integrarse rapidamente a un ambiente de desarrollo, definiendo pruebas de cualquier tipo.
Indice de Temas (tentativo):
* Acceptance Testing: Breve introduccion.
* Selenium IDE: Introduccion, demo y problemas.
* FitNesse
** FitNesse en solitario: Instalacion, introduccion, arquitectura, formato de la Wiki, ejemplos incluidos
** Extensibilidad con .net
** Integracion con Selenium RC
* Versionado de las pruebas
* Integracion de FitNesse con NAnt y CruiseControl.NET
Pueden leer más sobre los temas en:
http://www.fitnesse.org/
http://seleniumhq.org/
http://en.wikipedia.org/wiki/Selenium_(software)
http://seleniumhq.org/projects/ide/
http://en.wikipedia.org/wiki/Acceptance_testing
http://www.extremeprogramming.org/rules/functionaltests.html
http://www.extremeprogramming.org/rules/userstories.html
Más información sobre cómo asistir a la reunión virtual, software necesario, enlaces: http://altnet-hispano.pbworks.com/Descripcion-de-Reuniones
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
En estos días estuve reimplementando el núcleo de mi intérprete Lisp de código abierto AjLisp. Había escrito sobre la anterior versión en el 2008:
AjLisp: a Lisp interpreter in .NET
AjLisp: un intérprete Lisp en .NET
Es trabajo en progreso. Pueden bajar el código de:
http://code.google.com/p/ajlisp/source/browse/#svn/trunk/AjLisp
(hay otras dos implementaciones en ese repositorio, en desarrollo: AjScheme, un Lisp tipo Scheme, y AjSharpure, que intenta seguir las ideas de Clojure). Debería escribir sobre esas implementaciones. Este post es el inicial en una serie sobre AjLisp. Es una corta introducción al estado actual del proyecto.
El principal cambio en esta nueva versión: el intérprete puede manejar objetos y valores nativos. La alternativa hubiera sido apuntar a esos objetos a través de “wrappers”, alguna clase que implemente SymbolicExpression o similar. Pero elegí manejar directamente objetos nativos. Para eso, cambié las primitivas y clases relacionadas para que recibieran como argumentos objetos en vez de IExpression. Sigue habiendo una interfaz IExpression, defined como:
public interface IExpression
{
object Evaluate(ValueEnvironment environment);
}
El ValueEnvironment (planeo cambiarle el nombre a BindingEnvironment, y derivarlo de alguna interfaz tipo IBindingEnvironment) mantiene una asociación anidada de nombres y valores:
Hay un interfaz IFunction:
public interface IFunction
{
object Apply(List arguments, ValueEnvironment environment);
}
que debería ser implementada por cualquier form expression (la cabeza de una lista a evaluar).
Las dos clases principales que representan los tipos núcleo del AjLisp son identificadores y listas:
Todo el proyecto fue armado siguiendo Test-Driven Development, así que no tuve problemas en cambiar la versión anterior: tenía toda una batería de tests que me ayudó en el proceso de refactoring. Este es el estado actual de los test:
Debería escribir sobre:
- La implementación de ValueEnvironment
- Manejo de objetos nativos
- El tipo List y su evaluación
- Evaluación de Identifier
- Las primitivas implementadas
- El Lexer y el Parser
- Operaciones numéricas
- Números racionales (AjLisp puede manejar pares enteros numerador/denominador)
Ahora, luego de lo que aprendí desarrollando estos proyectos (AjLisp, AjScheme, AjSharpure), estoy escribiendo un núcleo mínimo AjCoreLisp, para mostrar cuáles son las primitivas mínimas para crear un intérprete Lisp. Armado con esa implementación, me gustaría explorar alternativas de compilación, en vez de ser sólo intérprete. Mi primer candidato es Dynamic Language Runtime. Otro podría ser la producción de código C# directo. Sería demasiado encarar esa exploración sobre un intérprete “más grande”. Primero, entonces, estudiaré sobre compilar un intérprete con menos primitivas, más condensado.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
En los últimos meses, estuve activo escribiendo intérpretes Lisp. Desde los ochenta, me gusta escribir ese tipo de intérpretes. Uno de mis ejercicios preferidos cuando estudio un nuevo lenguaje de programación, es escribir un intérprete Lisp en ese lenguaje (otro ejercicio es escribir un intérprete del lenguaje en estudio).
En el 2008, publiqué un código escrito en C#, AjLisp:
AjLisp: a Lisp interpreter in .NET
AjLisp: un intérprete Lisp en .NET
A la mitad del 2009, comencé a investigar Clojure. Dedicí explorar las nuevas ideas de ese lenguaje, reinplementánlo en C# (el Clojure original está escrito en Java, pero hay una versión en .NET, que compila usando DLR, Dynamic Language Runtime). Clojure compila a bytecodes Java. Mi versión es sólo un intérprete (debería estudiar y aprender sobre DLR o compilación .NET, y sobre cómo pasar el árbol de mi intérprete a código compilado). El resultado de todo esto es “work in progress”, se llama AjSharpure (se llamaba AjClojure, pero tuve que cambiar el nombre). Pueden ver el código, en desarrollo, en:
http://code.google.com/p/ajlisp/source/browse/#svn/trunk/AjSharpure
Inicialmente, mi idea era portar “uno a uno” el código de Java del Clojure original, pero me dí cuenta al tiempo que su código base no es fácilmente leíble, no tiene test en código, ninguna especificación acerca de la conducta esperada de docenas de clases y centeneares de métodos. Así que paré el desarrollo, y repensé el camino a seguir. Tomé coraje, olvidé el código inicial, y comencé de nuevo, de abajo hacia arriba, usando a fondo Test-Driven Development (TDD), implementando pieza por pieza, para ir consiguiendo parte por parte la conducta esperada externa del lenguaje, en vez de seguir de cerca su implementación interna.
Ahora está algo tranquilo el desarrollo de AjSharpure, porque estoy estudiando formas de implementar lo que me falta, como variables, Shared Transactional Memory, y otros temas, como References. Mientras, volví a mi versión 2008 de AjLisp. Escribí una nueva versión en 2009, haciendo un refactoring importante, armado de lo que había aprendido con AjSharpure. Ahora, tiene acceso y manejo de objetos y valores nativos. El trunk está en:
http://code.google.com/p/ajlisp/source/browse/#svn/trunk/AjLisp
No hay un Lisp “base”, y hay DOS principales dialectos: Common Lisp y Scheme. No me gustan algunas cosas de Common Lisp (debería escribir un post sobre mi razones), así, que para conocer más sobre Clojure y otros Lisp actuales, comencé a escribir AjScheme, un intérprete que implementa mucho de lo que es la referencia de Scheme (renuncié a implementar definición de macros como en el Scheme original, uso mis más conocidas mlambdas y expansión de macros):
http://code.google.com/p/ajlisp/source/browse/#svn/trunk/AjScheme
El AjLisp nació siguiendo un viejo libro de Christian Queinnec (un libro de los ochenta, no uno de sus más nuevos). No pude encontrar el código del libro en la web. Queinnec incluía en el texto el código de un intérprete Lisp escrito en su propio Lisp. No tengo el libro acá (lo tengo en mi segundo cubil). Me puse a investigar sobre lo mínimo que necesita ser implementado en un Lisp básico. Así que estos días estoy trabajando en el AjCoreLisp:
http://code.google.com/p/ajlisp/source/browse/#svn/trunk/AjCoreLisp
No tiene parser ni lexer. La idea es implementar un intérprete mínimo usando AjCoreLisp como la base: el resultado es MinimaLisp, y está incluido en ese trunk. Estoy implementando las mínimas primitvas, y estoy tratando de escribir el resto de las forms más comunes como funciones o macros. Una decisión “geek” ;-) : implementar la expasión de macros con backspace escribiéndola como una macro! El resultado en:
http://pastie.org/765371
Una vez que tenga AjCoreLisp/MinimaLisp estabilizado, haré refactor de AjLisp, AjScheme, para usar el kernel más pulido, pero agregaré como primitivas las forms más usadas. Debería medir el impacto de implementar forms más usadas como macros, en lugar de primitivas. Tengo confianza en poder hacer todos esos cambios, porque todo lo escrito está vigilado por una batería de tests (el haber seguido TDD y tener tests me ha salvado el día muchas veces, cuando he refactorizado mucho del código base).
Como próxima misión: escribir posts sobre estos diferentes intérpretes, variaciones sobre un leit-motiv: las hermosas ideas del lenguaje Lisp.
Para los que quieran estudiar más sobre Lisp, mis enlaces sobre el tema:
http://delicious.com/ajlopez/lisp
http://delicious.com/ajlopez/scheme
http://delicious.com/ajlopez/clojure
http://delicious.com/ajlopez/commonlisp
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
En post anteriores:
Canales y GoRoutines en AjSharp (Part 1)
Canales y GoRoutines en AjSharp (Part 2)
describí la implementación de gorutinas (goroutines, como en el lenguaje Go de Google), y canales en mi intérprete AjSharp. Al final del año que pasó, escribí una prueba rápida, implementando los mismos conceptos, pero para ser consumidos esta vez desde C# directamente. Pueden ver el código en
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjConcurr
Primero, porté la clase Channel:
public class Channel
{
private AutoResetEvent sethandle = new AutoResetEvent(false);
private AutoResetEvent gethandle = new AutoResetEvent(false);
private object value;
public void Send(object value)
{
this.gethandle.WaitOne();
this.value = value;
this.sethandle.Set();
}
public object Receive()
{
this.gethandle.Set();
this.sethandle.WaitOne();
object result = this.value;
return result;
}
}
El código tiene una clase estática GoRoutines, con métodos como:
public static void Go(Action action)
{
Thread thread = new Thread(new ParameterizedThreadStart(GoRoutines.RunAction));
thread.IsBackground = true;
thread.Start(action);
//ThreadPool.QueueUserWorkItem(new WaitCallback(RunAction), action);
}
public static void Go(ITask task)
{
Thread thread = new Thread(new ParameterizedThreadStart(GoRoutines.RunTask));
thread.IsBackground = true;
thread.Start(task);
//ThreadPool.QueueUserWorkItem(new WaitCallback(RunTask), task);
}
Pueden lanzar una Action (lo que es en el framework delegate System.Action<>), en un nuevo thread (traté de hacerlo poniendo la tarea en la cola de ThreadPool, pero, no sé por qué, el rendimiento pasó a ser muy bajo; mi intento quedó en comentarios del código de arriba).
Cuando una acción recibe parámetros, se encapsulan los dos en una Task<>, como esta que recibe dos parámetros:
public class Task<T1, T2> : ITask
{
private Action<T1, T2> action;
private T1 parameter1;
private T2 parameter2;
public Task(Action<T1, T2> action, T1 parameter1, T2 parameter2)
{
this.action = action;
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
public void Run()
{
this.action(this.parameter1, this.parameter2);
}
}
(hay clases Task para uno, dos o tres parámetros).
Pueden invocar GoRoutines.Go directamente, especificando una acción y sus parámetros:
public static void Go<T1, T2>(Action<T1, T2> action, T1 parameter1, T2 parameter2)
{
Go(new Task<T1, T2>(action, parameter1, parameter2));
}
Podemos escribir esta invocación usando expresiones lambda:
[TestMethod]
public void RunGoRoutineWithTwoParameters()
{
int i = 0;
AutoResetEvent handle = new AutoResetEvent(false);
GoRoutines.Go((x, y) => { i = x + y; handle.Set(); }, 2, 3);
handle.WaitOne();
Assert.AreEqual(5, i);
}
Con todo esto implementado, escribí una aplicación de consola AjConcurr.Primes, reimplementando el ejemplo de números primos de mi anterior post:
Channel numbers = new Channel();
GoRoutines.Go(() => { for (int k = 2; ; k++) numbers.Send(k); });
Channel channel = numbers;
int prime = 0;
while (prime < 1000)
{
prime = (int)channel.Receive();
Console.WriteLine(prime);
Channel newchannel = new Channel();
GoRoutines.Go((input, output, p) =>
{
while (true)
{
int number = (int)input.Receive();
if ((number % p) != 0)
output.Send(number);
}
}, channel, newchannel, prime);
channel = newchannel;
}
Me gusta ver este código formateado en pastie:
http://pastie.org/761916
Próximos pasos:
- Mejorar Channel para soportar múltiples productores y consumidores de valores que operan de forma simultánea sobre el mismo canal.
- Agregar soporte de Futures
- Agregar características de reactive programming (sé que está el Reactive Framework, quería experimentar algo con código propio).
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
En mi anterior post describí algo de la implementación de canales y “goroutines” en AjSharp, mi intérprete de un lenguaje de scripting. Quisiera hoy mostrar algunos ejemplos del uso de esos canales y rutinas lanzadas en paralelo.
Primero, recordemos el código simple:
channel = new Channel();
go channel <- 10;
result = <- channel;
En la primera línea, se crea el canal. Luego, el comando go ejecuta en otro thread el envío del valor 10 al canal. En la última línea se toma el valor desde el canal, y se coloca en la variable result. channel <- 10 es “syntax sugar” para channel.Send(10). <-channel es una expresión (devuelve un valor, no es un comando, se puede colocar en cualquier lugar donde se espera una expresión), que codifica channel.Receive(). Las operaciones Send y Receive son bloqueantes: cuando enviamos un valor a un canal, si no hay otro thread leyendo, el thread que envía queda bloqueado, y viceversa. Esta conducta nos da una forma de coordinación entre el productor de valores para el canal y el consumidor de esos valores. Tengo que mejorar el código de implementación para soportar que varios productores (en distintos threads) puedan enviar valores a un mismo canal. Lo mismo para varios consumidores.
Podemos usar el canal varias veces:
channel = new Channel();
go for (k=1; k<=5; k++) channel.Send(k);
for (j=1; j<=5; j++)
result = result + channel.Receive();
que en notación de operadores, es lo mismo que:
channel = new Channel();
go for (k=1; k<=5; k++) channel <- k;
for (j=1; j<=5; j++)
result = result + <-channel;
En vez de generar solamente cinco valores, podemos escribir una goroutine que escriba todos los números en un canal:
channel = new Channel();
running = true;
k = 0;
go while(running) channel <- k++;
for (value = <-channel; value<=10; value = <-channel)
PrintLine(value);
La goruoutine no ejecuta por siempre: su ejecución es controlada, indirectamente, por el thread principal, usando las operaciones bloqueantes del canal. Si la goroutine intenta escribir en el canal, pero no hay quien esté leyendo, entonces la goroutine se bloquea.
Más interesante, podemos usar varios canales para comunicar distintos subprocesos:
channel = new Channel();
running = true;
k = 0;
go while(running) channel <- k++;
function filter(in, out)
{
while (true)
{
value = <-in;
PrintLine("Received in filter " + value);
if (value % 2)
out <- value;
}
}
odds = new Channel();
go filter(channel, odds);
for (number = <-odds; number <= 7; number = <-odds)
PrintLine("Received in main " + number);
running = false;
Este código crea un canal, y un thread paralelo que envía números naturales por ese canal. Otro thread toma valores del primer canal, filtra los impares (rechazando los pares) y los envía a un segundo canal. El thread principal toma valores de este segundo canal, e imprime los primeros resultados.
Vemos que podemos crear, conectar y usar muchos canales. El ejemplo que sigue (inspirado en el que brinda la gente de Google para el Go language ver http://golang.org/doc/go_tutorial.html#tmp_346) imprime los números primos menores de 1000:
numbers = new Channel();
running = true;
k = 1;
go while(running) { k++; numbers <- k; }
function filter(in, out, prime)
{
while (true)
{
value = <-in;
if (value % prime)
out <- value;
}
}
function makefilter(channel, number)
{
newchannel = new Channel();
go filter(channel, newchannel, number);
return newchannel;
}
channel = numbers;
number = <-channel;
while (number < 1000)
{
PrintLine("Prime " + number);
channel = makefilter(channel, number);
number = <-channel;
}
running = false;
Esto planeando agregar un comando done, para parar las goroutines que quedaron andando (una idea más liviana es que las goroutines se lanzen en threads de background). Por ahora, no necesité esa característica. Tengo que estudar el lenguaje Go más en detalle: al parecer, las goroutines se ejecutan en un solo thread, pero no estoy seguro. Para leer más:
http://scienceblogs.com/goodmath/2009/11/the_go_i_forgot_concurrency_an.php
Sutil tema: en el código de arriba, escribí una función makefilter. En mi primer intento, escribí ese código en línea, no como función, directamente dentro del ciclo while, pero tuve un problema: la rutina lanzada por go tiene acceso a las variables que estan en su “lexical scope”, y entonces, cuando la go ruitina accede, por ejemplo, a la variable channel, ese valor podría no ser el mismo que tenía cuando se pasó por el go (la rutina de dentro del go se ejecuta en paralelo), el código externo podría haberlo cambiado. La solución: escribir una función makefilter (que no es otra cosa que un lambda con nombre) para recibir y mantener el valor original de channel y otros valores, sin verse afectados por la rutina principal. En el ejemplo del lenguaje Go que cité arriba, el autor escribe la manipulación del canal en línea, sin apelar a una rutina (por lo menos en el primer ejemplo que muestra), así que debería estudiar más en detalle este tema.
Pueden bajar el código actual de AjSharp desde http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage. Los ejemplos presentados acá están en AjSharp.Tests/Examples y en AjSharp.Console/Examples (AjSharp.Console es una aplicación de consola que permite ejecutar programas AjSharp leyendo archivos o ingresando el código interactivamente).
Me gusta cómo queda el código del ejemplo de números primos en pastie http://pastie.org/758175 ;-)
Mis próximos pasos en canales y gorutinas: implementarlos y usarlos directamente en C# (ayer escribí un spike), y agregar a un proyecto como el AjAgents o similar. Pueden ver el spike en:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjConcurr
y el código de ejemplo de números primos en C# directo:
http://pastie.org/761916
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Hace dos años ya, estuve explorando Microsoft Robotics, y su librería CCR (Concurrency and Coordination). Esta tiene la implementación una port: podemos enviar un objeto por el port, y recibirlo y procesarlo en otra pieza de código, que puede ejecutarse en otro hilo de ejecución. También tiene la capacidad de ejecutar ambas partes en diferentes máquinas, una forma de conectar aplicaciones distribuidas. Yo escribí sobre el tema en:
Distributed Agents using DSS/VPL
Agentes Distribuidos usando DSS/VPL
Agentes Distribuidos y Fractales usando DSS/VPL
Genetic Algorithms with AjAgents and Concurrency and Coordination Runtime (CCR)
Algoritmos Genéticos con AjAgents y Concurrency and Coordination Runtime (CCR)
CCR posts (English)
Posts sobre CCR en español
Todo esto, me llevó a encontrarme con el concepto de canal: Channel programming (Wikipedia) y este año, leí sobre el nuevo lenguaje de Google, el lenguaje Go: http://golang.org. Pueden leer sobre Go y sus capacidades de manejo de concurrencia (go routines, channels) en: http://golang.org/doc/go_tutorial.html#tmp_346
También comencé a leer sobre proyecto Axum de Microsoft: http://en.wikipedia.org/wiki/Axum_%28programming_language%29
Con toda esta base, el último sábado agregué soporte de canales y goroutines en mi intérprete AjSharp (otros posts AjSharp interpreter). Pueden bajar y ver la versión actual desde http://code.google.com/p/ajcodekatas en trunk/AjLanguage
Primero, agregué un tipo nativo .NET Channel (a mejorar: agregar soporte para que el canal pueda soportar varios send y receive desde más de un thread):
public class Channel
{
private AutoResetEvent sethandle = new AutoResetEvent(false);
private AutoResetEvent gethandle = new AutoResetEvent(false);
private object value;
public void Send(object value)
{
this.gethandle.WaitOne();
this.value = value;
this.sethandle.Set();
}
public object Receive()
{
this.gethandle.Set();
this.sethandle.WaitOne();
object result = this.value;
return result;
}
}
Cuando se envía un objeto a un canal, el thread que lo envía se bloquea hasta que otro thread lea el canal (pienso agregar un QueueChannel, donde se puedan enviar varios objetos, a guardar en una cola, sin para los threads; supongo que la cola tendrá un tamaño limitado, para ir auto-coordinando los productores y consumidores de objetos del canal).
Siguiendo las ideas de goroutines en el lenguaje Go, agregué un nuevo comando en AjSharp: go <command> (ok, no mucha creatividad aquí… ;-)… igual hay que aclarar que los goruotines en Go tienen muchas más características que las que implemento acá). Esta nueva palabra lanza el comando en un thread separado, que comparte las variables del original, como en este ejemplo (recordemos que AjSharp tiene acceso a los objetos y clases nativas de .NET):
handle = new System.Threading.AutoResetEvent(false);
one = 0;
go { one = 1; handle.Set(); }
handle.WaitOne();
result = one;
Todo esto se agregó a la máquina de ejecución de AjSharp (por ejemplo, Channel es un tipo ya declarado dentro de la máquina, sin necesidad de usar el namespace completo), podemos usar entonces:
channel = new Channel();
go channel.Send(10);
result = channel.Receive();
Luego de tener esto andando, modifiqué algo más. Fue simple agregar “syntax sugar” al parser de AjSharp para que
<- channel
sea igual a
channel.Receive()
y que
channel <- value;
sea una forma de escribir:
channel.Send(value);
Entonces, el ejemplo anterior se puede escribir como:
channel = new Channel();
go channel <- 10;
result = <- channel;
En otro post, describiré algunos usos de estas características, con algún detalle de implementación. Ver el código actual en:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/AjLanguage
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Estoy escribiendo un intérprete no tipado con sintaxis tipo C#, que bauticé AjSharp. Hace un tiempo escribí sobre este proyecto de código abierto en:
AjSharp Programming Language a C#-like Dynamic Language
AjSharp: un lenguaje dinámico en C#
Pueden bajarse la versión actual desde http://code.google.com/p/ajcodekatas en trunk/AjLanguage (AjLanguage define el árbol de ejecución del intérprete, AjSharp tiene un parser, un lenguaje definido para construir ese árbol; la idea es colocar otros parsers sobre AjLanguage, por ejemplo, AjBasic).
Ayer sábado, agregué una nueva característica, la definición de un objecto. Ya de antes, AjSharp soporta la definición de una clase dinámica:
class Person
{
var Name;
var Age;
function AddYears(years)
{
this.Age = this.Age + years;
}
}
Pueden crear un objecto usando:
adam = new Person();
adam.AddYears(800);
adam.Name = "Adam";
El objeto creado es dinámico: se le pueden agregar variables de instancias y métodos, en cualquier momento:
adam.LastName = "unknown";
adam.EyeColor = "brown";
adam.SayHello = function() { PrintLine("Hello"); };
Pueden crear objetos, con esta notación, inicializando su estado:
adam = new Person() { FirstName = “Adam”, LastName = “Genesis”, Age = 800 };
Y hasta pueden definir un objeto sin tener una clase asociada:
dynobj = new { Name = “Adam”, Age = 800 };
Esto es equivalente a hacerlo más “programáticamente” así:
dynobj = new DynamicObject();
dynobj.FirstName = “Adam”;
dynobj.LastName = "Genesis”;
dynobj.Age = 800;
dynobj.FullName = function() { return FirstName + “ “ + LastName; }
Esto estaba implementado desde hace meses. Ayer agregué la notación:
object Adam
{
var Name = "Adam";
var Age = 700;
function AddYears(years)
{
this.Age = this.Age + years;
}
}
Adam.AddYears(100);
donde se puede definir un objecto único, dándole estructura, conducta y nombre.
La idea de AjLanguage es que pueda acceder a todas las clases y objetos del framework subyacente (en este caso, .NET, pero está pensado para ser reescribible sobre Java, si fuera necesario, claro, cambiando cualquier referencia a tipos nativos):
ds = new System.Data.DataSet();
dinfo = new System.IO.DirectoryInfo(“.”);
foreach (fi in dinfo.GetFiles())
{
PrintLine(fi.FullName);
}
Desde ayer, seguí implementando constructores. Tengo pensado definir visibilidades privadas y públicas (por ejemplo, las variables y métodos de instancia que comiencen con minúscula, hacerlas automáticamente privadas). También agregar propotipos, como en Javascript, lo que permitiría implementar cosas similares a los métodos de extensión de .NET.
Pero si van hoy al trunk, encontrarán que estuve implementando canales, como en en lenguaje Go de Google.
channel = new Channel();
go for (k=1; k<=5; k++) channel <- k;
for (j=1; j<=5; j++)
result = result + <-channel;
Pero eso dará para otro post más detallado. Con esa característica quiero explorar temas como programación reactiva, agentes, actores, shared transactional memory, ver:
http://clojure.org/Agents
http://en.wikipedia.org/wiki/Actor_model
http://en.wikipedia.org/wiki/Axum_%28programming_language%29
Por ejemplo, sería interesante que los canales comunicaran no sólo dos códigos en paralelos, sino máquinas diferentes. Y definir esas máquinas en tiempo de deployment, sin cambiar el código del programa. Podría reescribir los ejemplos de Distributed Agents using DSS/VPL o Genetic Algorithms with AjAgents and Concurrency and Coordination Runtime (CCR)
Como verán, me estoy divirtiendo!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Una de las prácticas que siempre intento “vender” a cualquiera involucrado en el desarrollo de software, es la práctica de Test-Driven Development. Una vez que se practica TDD, no quieren volver atrás. Es una forma de programar con tanta recompensa y gozo asociados, que me siento incómodo si no lo usa cuando escribo software real.
Gracias a un tweet de ayer de @jjfalcon, descubrí este ejemplo, en Youtube, del usuario objarni. El usa Ubuntu, programando en Python, usa pyTDDemon para ver inmediatamente el resultado de los tests.
Implementa algo sencillo: código que dado un string con una URL, identificar el protocolo, el dominio, y el recurso que está contenida en esa dirección. En TDD, se va escribiendo el test, el código que pasa el test, y se va progresando de a poco. No hace falta escribir el código correcto y completo desde el principio. Como en otras tantas actividades, el “baby-step”, el “pequeños pasos” de avance, nos ayuda para ir incrementalmente produciendo el resultado esperado.
Noten el ciclo rojo-verde-refactor, el código mínimo que se agrega en cada tests (a veces, retornando valores puestos a mano, sólo para pasar los tests), refactorizando el test si hay código duplicado, las micro-decisiones de diseño que se van tomando, etc… Excelente trabajo para mostrar en video!
Habría tanto para comentar de TDD. Por ahora, dos recursos. He “tagueado” más videos sobre TDD, en mi delicious:
http://delicious.com/ajlopez/tdd+video
Los enlaces que colecciono sobre TDD:
http://delicious.com/ajlopez/tdd
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
More Posts
Next page »