Stab : un langage pour découvrir C# quand on ne connait que java

Ce billet est issu de la concomitance de deux évènements :

  • je me pose la question "comment peut-on écrire quelque chose proche de C# mais qui tourne avec une JVM ?" et je découvre le langage "stab"
  • je lis ce billet "Sélection FooBarQix" où un même programme a été implémenté dans 13 langages différents ayant pour seul point commun la JVM.

Il ne m'en a pas fallu plus pour écrire un FooBarQix en stab.
Et je crois que ce programme a un intérêt pour montrer ce que l'on peut écrire quand on rajoute quelques éléments basiques de C# 3 à java 6.
Le code complet est dispo sur github.

J'ai fait au plus simple avec seulement deux fichiers.

Un fichier de tests qui utilise junit :

using java.lang;
using junit.framework;
 
public class TestFooBarQix : TestCase
{
  delegate void Action<T1,T2>(T1 t1, T2 t2);

  public void testFBQ()
  {
    Action<Integer,String> check = (src,dst) => { assertEquals(src+" => "+dst, dst, FooBarQix.from(src)); };
    check(1,"1");
    check(2,"2");
    check(3,"FooFoo");
    check(4,"4");
    check(5,"BarBar");
    check(6,"Foo");
    check(8,"8");
    check(9,"Foo");
    check(10,"Bar");
    check(11,"11");
    check(12,"Foo");
    check(13,"Foo");
    check(15,"FooBarBar");
    check(7,"QixQix");
    check(14,"Qix");
    check(16,"16");
    check(17,"Qix");
    check(18,"Foo");
    check(19,"19");
    check(20,"Bar");
    check(21,"FooQix");
  }
}

Et un fichier de code qui tire profit de quatre éléments majeurs de C# :

  • les lambda expressions
  • les générateurs
  • les types anonymes
  • LINQ (qui n'est presque rien de plus que la résultante des éléments précédents)
using java.lang;
using stab.query;
 
public static class FooBarQix
{
  public static String from(Integer numberToConvert)
  {
    var fbqDigits = sequence(
      new { Value=3, Text="Foo" },
      new { Value=5, Text="Bar" },
      new { Value=7, Text="Qix" }
    );

    Predicate<Integer> isNumberToConvertDivisibleBy = n => (numberToConvert % n == 0);
    var divisors = fbqDigits.where(x => isNumberToConvertDivisibleBy(x.Value));

    var digitsReplacement=digitsLeftToRight(numberToConvert).selectMany(digit => fbqDigits.where(x => x.Value==digit));

    var fbqConversions = divisors.concat(digitsReplacement);
 
    if( fbqConversions.count() == 0 )
      return Integer.toString(numberToConvert);
    else
      return fbqConversions.select(x => x.Text).aggregate((a,b)=>a+b);
  }

  static Iterable<Integer> digitsLeftToRight(Integer number)
  {
    return digitsRightToLeft(number).reverse();
  }

  static Iterable<Integer> digitsRightToLeft(Integer number)
  {
    while(number != 0)
    {
      yield return number % 10;
      number = number / 10;
    }
  }

  delegate Boolean Predicate<T>(T t);

  static Iterable<T> sequence<T>(params T[] items)
  {
    return Query.asIterable(items);
  }
}