giovedì 28 aprile 2011

Da Scala a LINQ

Spesso, durante il mio lavoro diurno (C# e Javascript) mi trovo a rimpiangere gli strumenti di cui dispongo nel mio lavoro notturno (Scala e Java).

Stavolta è andata così. Mi trovavo spesso a scrivere codice non transazionale in cui devo fare un'operazione su una serie di oggetti ma le eccezioni dovevano essere gestite ed aggregate, in questo modo:


var erroriGestiti = new List<QualcheCosa>();

foreach( var item in items ) {
   try {
      FaiQualcosaCon(item);
   }
   catch( QualcheTipoDiEccezione excp) {
      a.Add( CreaQualcheCosaDa(excp) );      
   }
}

return erroriGestiti;
Vigili del Fuoco (Vatican) - fleet Ed ero ovviamente stufo di ripetere questo costrutto. Ora, in Scala astrarre il concetto "collezionare le eccezioni di operazioni in sequenza" è un gioco da ragazzi, per cui lo tralascio. Però, anche se dire che LINQ ti permette di fingere di essere in Scala sarebbe un'assurdità (neanche F# te lo fa dire), può darti una mano:

public static 
IEnumerable<TReturn> CollectErrors<TItem, TException, TReturn>(
       this IEnumerable<TItem> items, 
       Action<TItem> act, 
       Converter<TException, TReturn> exceptionHandler
)
      where TException : Exception
{
   if (items == null) 
       throw new ArgumentNullException("items");
   if (act == null) 
       throw new ArgumentNullException("act");
   if (exceptionHandler == null) 
       throw new ArgumentNullException("exceptionHandler");

   return items
        .Aggregate(new List<TReturn>(),
                   (collector, item) =>
                   {
                     try
                     {
                       act(item);
                     }
                     catch (TException ex)
                     {
                       collector.Add(exceptionHandler(ex));
                     }
                     return collector;
                   });
}

// e questo mi permette di scrivere:

[TestMethod]
public void TestMethod1()
{
   var expected = new[] { "error", "error", "error" };

   var actual = new[] { 1, 2, 3, 4, 5, 6 }
       .CollectErrors(i => { if (i > 3) 
                               throw new IndexOutOfRangeException(); 
                             },
                      (IndexOutOfRangeException e) => "error");

   Assert.IsTrue(actual.SequenceEqual(expected));
}

certo non è una sintassi molto chiara come potrebbe essere in Scala, ma non è poi tanto male, no?

Nessun commento: