Cela fait maintenant pas mal de temps que j’entends parler de Scala, mais je n’avais jamais pris le temps m’y attarder. Il y a maintenant deux semaines, l’annonce du lancement de Typesafe (on pourra lire l’article de Nicolas pour plus d’informations) m’a donné envie de regarder de plus près ce langage.

En attendant mon prochain passage par la FNAC pour trouver un bon livre sur la question, j’ai été glaner quelques informations pour débuter :

J’ai eu la chance d’avoir des cours de Caml Light en prépa et de Lisp en école d’ingénieurs : la programmation fonctionnelle ne m’est donc pas inconnue, ce qui m’a grandement facilité l’entrée en matière (même si j’ai rapidement dû me mettre à niveau avec les Case Class et autres concepts que je n’avais encore jamais rencontré).

Les cours de prépa que j’ai pu avoir sur la question étaient excellents. Même s’ils concernent le langage Caml, ils fournissent des exemples originaux dans lesquels la programmation fonctionnelle permet de s’en sortir bien plus facilement qu’en programmation procédurale. Je propose donc aux curieux d’aller les consulter sur le site de notre enseignant.

Problématique

Après ces premiers pas dans le langage à suivre des tutoriels, j’ai trouvé quelques cas d’utilisation dans mes projets sur lesquels le Scala serait mieux adapté que le Java. Il est bien évidemment hors de question de réécrire toute l’application en Scala. Je me suis donc demandé s’il était possible de faire cohabiter facilement dans une même application du code Java et Scala.

Je vais donc partir d’un projet Java standard, et tenter d’y ajouter du Scala petit à petit. Voici donc la structure de mon projet :

  • src/main/java : code source Java
  • src/main/scala : code source Scala

Choix de l’IDE

Typesafe préconise l’utilisation de Scala-IDE, basé sur Eclipse. J’avoue ne même pas l’avoir essayé : je n’ai donc pas de point de comparaison. Je me suis directement tourné vers le plugin Scala d’IntelliJ IDEA, et il me convient parfaitement :

  • coloration syntaxique
  • autocomplétion (y compris lorsque le paramètre est une fonction)
  • détection des erreurs de compilation à la volée
  • possibilité d’utiliser du Scala et du Java dans un même projet, avec des appels croisés
  • etc.

Bref… je retrouve dans le plugin Scala tous les petits plus que j’apprécie dans IntelliJ par rapport à Eclipse !

Appeler du code Scala depuis du Java

Après quelques tests, il semble qu’appeler du code Java depuis Scala ne pose apparemment aucun problème. Mais dans le cas qui m’intéresse d’une application Java pré-existante, c’est plus l’inverse dont je vais avoir besoin.

Prenons donc un code utilitaire en Scala :

1
2
3
4
5
6
7
object ListUtils {
    def length[A](ls: List[A]): Int =
        ls match {
            case Nil => 0
            case _ :: tail => 1 + length(tail)
        }
}

Premier test simpliste en Java :

1
2
3
4
5
6
public static void main(String[] args) {
    java.util.List<String> listeTest = new java.util.ArrayList<String>();
    listeTest.add("un");
    listeTest.add("deux");
    System.out.println("Taille : " + ListUtils.length(listeTest));
}

On tente de compiler, et les premiers soucis ne se font pas attendre :

<A>length(scala.collection.immutable.List<A>) in org.courtine.scala.ListUtils cannot be applied to <java.lang.String>(java.util.List<java.lang.String>)

L’erreur a le mérite d’être explicite. Il ne nous reste donc qu’à convertir notre java.util.List en scala.collection.immutable.List. Je n’ai pas encore réussi à faire cette conversion côté Java. En revanche, c’est une conversion assez simple côté Scala. Il suffit donc de créer dans notre classe utilitaire Scala une méthode supplémentaire :

1
def length[A](ls: java.util.List[A]): Int = length(ls.toArray.toList)

On recompile et on exécute l’ensemble : ça marche !

Après ce test utilitaire, un autre test concluant : une classe métier Scala peut être instanciée et utilisée directement dans le code Java, sans aménagement particulier (du moins une classe simple : je n’ai pas encore essayé de mettre en défaut cette compatibilité avec des cas tordus).

Automatiser le build

Ces premiers tests étant concluants, passons à l’étape du build : l’exécution des tests se passe bien dans IntelliJ, mais une fois sorti de l’IDE, peut-on assurer un build automatisé (tests, intégration continue, etc.).

Pour les projets sur lesquels je travaille, le build est assuré par Maven. Après une recherche rapide, on découvre qu’il existe un plugin Scala pour Maven. Voici donc le fichier pom de mon projet :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.courtine</groupId>
    <artifactId>scala-java-test</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <name>scala-java-test</name>
    <repositories>
        <repository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>scala-tools.org</id>
            <name>Scala-tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <phase>compile</phase>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                        <phase>test-compile</phase>
                    </execution>
                    <execution>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Ce fichier automatise convenablement le packaging de l’application par Maven.

Et maintenant

D’après ces premiers tests, il semble donc qu’on puisse créer une application mélangeant du code Scala et Java, avec un coût d’entrée faible (du point de vue des adaptations à effectuer : j’exclue ici le coût d’apprentissage du langage Scala lui-même, qui est loin d’être négligeable).

La prochaine étape sera d’essayer de vérifier si un tel mélange de codes n’est pas préjudiciable aux performances.

L’étape suivante sera certainement la plus compliquée : convaincre un client des avantages d’un tel mélange (code plus expressif sur les parties métier du projet), pour pouvoir le mettre en oeuvre sur un véritable projet.