dimanche 20 janvier 2013

Installer Scala sous Linux

Dans ce court post je partage comment j'installe Scala sur mon PC tournant sous Ubuntu. Commencez d'abord par télécharger depuis le site web de Scala le fichier archive (au format .tgz) de la dernière version du langage : http://www.scala-lang.org/downloads. La version actuelle de Scala est la 2.10 et c’est celle que nous allons installer.

Voici comment l'obtenir via la ligne de commande :

$ wget http://www.scala-lang.org/downloads/distrib/files/scala-2.10.0.tgz
Ensuite nous extrayons le contenu du fichier archive :
$ tar xvfz scala-2.10.0.tgz
Les fichiers sont extraits dans le dossier scala-2.10.0. La sortie suivante de la commande tree montre la structure des fichiers.
$ cd scala-2.10.0
$ tree -L 1
.
├── bin
├── doc
├── examples
├── lib
├── man
├── misc
└── src

Les exécutables se trouvent dans le dossier bin et les librairies dans le dossier lib. Sur mon ordinateur les fichiers se trouvent dans ~/tools/scala-2.10.0 sur lequel j’ai créé le lien symbolique /opt/scala puisque je souhaite pouvoir changer facilement de version de Scala.

$ sudo ln -s ~/tools/scala/scala-2.10.0 /opt/scala

Vérifions l’installation en affichant la version de Scala :

$ /opt/scala/bin/scala -version
Scala code runner version 2.10.0 -- Copyright 2002-2012, LAMP/EPFL
Ça marche mais ce n’est pas pratique de taper le chemin complet de la commande à chaque lancement de l’interpréteur Scala. Afin de corriger cela j’ajoute le dossier /opt/scala/bin au chemin du système avec la commande suivante :
$ export PATH=$PATH:/opt/scala/bin
En mettant la ligne précédente dans le fichier .bashrc les exécutables se trouvant dans /opt/scala/bin seront ajoutés au chemin du système à l’ouverture d’une session.
$ scala
Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).
Type in expressions to have them evaluated.
Type :help for more information.
scala> println ("Now go play with Scala !")
Now go play with Scala !

samedi 12 janvier 2013

Venez apprendre les langages de programmation avec coursera

Après le cours de Martin Odersky sur la programmation fonctionnelle je m'apprête à suivre le cours de Dan Grossman, professeur à l'université de Washington, sur les langages de programmation. Je suis très excité et pressé de commencer ce cours le lundi 14 janvier. L'objectif est de munir les étudiants des clés nécessaires à la compréhension des concepts fondamentaux derrière les langages de programmation. Les langages suivants sont utilisés pour illustrer les propos :

  • ML : un langage fonctionnel à typage statique
  • Ruby : un langage orienté objet à typage dynamique
  • Racket : un langage de la famille des Lisp. Ce sera une vraie découverte pour moi ça !

A un moment où les langages de programmation pullulent notamment sur la plate-forme Java ce cours est le bienvenu pour ceux qui veulent comprendre les fondamentaux au lieu de s'arrêter seulement à la syntaxe des langages. La syntaxe va et vient mais les concepts restent. D'ailleurs la plupart des concepts que j'ai découverts en m'intéressant à la programmation fonctionnelle datent de quelques dizaines d'années. Bref, inscrivez-vous à ce cours depuis coursera.org, ça vous fera une bonne résolution de plus pour l'année 2013 !

samedi 8 septembre 2012

La gestion des erreurs avec scala.util.Try (2/2)

Après la découverte des bases de la gestion des erreurs avec scala.util.Try, nous allons, dans cette deuxième partie, explorer des fonctionnalités plus avancées. Nous utiliserons un exemple très simple pour illustrer nos propos : lancer un serveur sur un numéro de port fourni par l'utilisateur. Cette tâche se décompose en deux sous-tâches, la première consiste à convertir l'entrée (input dans le code) de l'utilisateur en un numéro de port et la seconde à démarrer le serveur sur ce port. Commençons par la première :

 
    val input = "..."
    val result = Try { input.toInt }
  
Encapsulons ce bout de code dans une fonction parsePort :
 
    def parsePort(input: String): Try[Int] = Try(input.toInt)
  
Comme nous l'avons vu dans la première partie le type de retour de cette méthode est scala.util.Success ou scala.util.Failure selon que la valeur saisie par l'utilisateur soit un nombre ou non. La méthode ci-dessous teste notre super méthode :
 
  @Test
  def parsePortTest() {
   assertTrue(parsePort("80").isInstanceOf[Success[_]])
   assertTrue(parsePort("toto").isInstanceOf[Failure[_]])
  }
  
Munis de cette méthode attaquons-nous à la deuxième sous-tâche : la transformation du numéro de port en un serveur sur ce port.

Transformer la valeur contenue dans Try

La méthode transform correspond à notre problématique. Voici sa signature :

 
  def transform[U](s: (T) ⇒ Try[U], f: (Throwable) => Try[U]): Try[U]
  
C'est une fonction d'ordre supérieur qui prend deux paramètres :
  • une fonction s qui s'applique lorsque le traitement dans Try a réussi. Dans notre cas cette fonction est définie comme suit :
     
        def handleSuccess = (port: Int) ⇒ Try(SimpleServer(port))
      
  • une fonction f qui s'applique lorsque le traitement a échoué. Nous utiliserons 80 comme numéro de port par défaut en cas d'erreur. Voici comment on la définit :
     
         def handleFailure = (t: Throwable) ⇒ Try(SimpleServer(80))
      
Le type SimpleServer ci-dessus est :
 
  case class SimpleServer(port: Int) {
   def start(): Unit = {
     println("Starting the server on port " + port)
     ...
   }
   ...
  }
  
Et enfin le test du code :
import org.junit.Test
import org.junit.Assert._
import scala.util.{Failure, Success, Try}

@Test
def transformTest() {
  //Transform the result
 def handleSuccess = (port: Int) ⇒ Try(SimpleServer(port))
 def handleFailure = (t: Throwable) ⇒ Try(SimpleServer(80))

 //Normal case : we create the server instance with the specified port
 val normalCase = parsePort("9090").transform(handleSuccess, handleFailure)
 assertEquals(Success(SimpleServer(9090)), normalCase)

 //Erroneous case : we create the server instance with the default port
 val erroneousCase = parsePort("toto") transform (handleSuccess, handleFailure)
 assertEquals(Success(SimpleServer(80)), erroneousCase)
}

Fournir un traitement par défaut en cas d'échec

La méthode recover permet de spécifier une fonction dont l'invocation fournit une valeur dans le cas où l'instance du Try est un objet scala.util.Failure.

def recover[U >: T](f: PartialFunction[Throwable, U]): Try[U]
Vous aurez remarqué que la fonction en question est une fonction partielle, et nous la définissons comme suit en ne prenant en compte que les exceptions de type NumberFormatException. Lorsqu'une telle exception est lancée nous utilisons le port 80.
import org.junit.Test
import org.junit.Assert._
import scala.util.{ Failure, Success, Try }

@Test
def recoverTest() {
 val erroneousCase = parsePort {
   "toto"
 } recover {
   case _: NumberFormatException ⇒ 80 // PartialFunction
 } flatMap { port ⇒
   Success(SimpleServer(port))
 }

 assertEquals(Success(SimpleServer(80)), erroneousCase)
}
So far so good ! Maintenant voyons comment appliquer un prédicat sur le contenu de Try.

Filtrer sur le contenu dans une instance de Try

Après avoir parsé le numéro renseigné par l'utilisateur nous devons d'abord vérifier qu'il n'est pas déjà utilisé par un autre service. La méthode filter sert à cela :

parsePort ("8080")
Nous filtrons le résultat obtenu avec la méthode filter de Try et le prédicat suivant :
def predicate(port: Int): Boolean = port != 8080
La méthode filter a la signature suivante :
def filter(p: (T) ⇒ Boolean): Try[T]
Elle prend un prédicat et transforme le Try en un objet de type Failure si le prédicat n'est pas satisfait. FilterTest résume tout cela :
@Test
def filterTest() {
  val erroneousCase = parsePort {
    "8080"
  } filter {
    predicate(_)
  }
  assertTrue(erroneousCase.isInstanceOf[Failure[_]])
}
No restons pas sur cet échec car la méthode recover est là pour permettre de spécifier une valeur par défaut, par exemple 8081, dans le cas où le filtrage échoue.
@Test
def filterTest() {
  val result = parsePort {
    "8080"
  } filter {
    predicate _
  } recover {
    case _ ⇒ 8081
  } map { port ⇒
    SimpleServer(port)
  }
  assertEquals(Success(SimpleServer(8081)), result)
}
Ce code fait beaucoup de choses. D'abord il parse la chaine "8080" et obtient Success(8080). Ensuite il transforme cet objet en un objet Failure avec la méthode filter car le prédicat n'est pas satisfait. La méthode recover fournit 8081 comme valeur par défaut et enfin map crée le serveur avec ce port.

Et pour la route

Ici prend fin notre découverte de scala.util.Try qui offre une API élégante pour gérer des traitements pouvant échouer. À ce point vous en savez assez pour aller plus loin.