🇫🇷 Scala léger avec Ammonite

2018-07-14

On entend souvent: "Scala c'est compliqué". J'ai tendance à dire pas tant que ça, on peut faire simple, on n'est pas obligé de faire de la programmation fonctionnelle de "haut vol" (si vous faites du JavaScript vous appliquez probablement des concepts fonctionnel).

On entend souvent: "Scala c'est lourd". Ok, sur un petit MacBook, il peut y avoir comme une souffrance au moment de la compilation. Donc si vous n'avez pas une configuration de course, vous hésitez à vous y mettre (je comprends, c'est meiux d'être confortable pour coder).

Une solution existe pour vous 🎉: Ammonite et plus précisément Ammonite-REPL (REPL:Read–Eval–Print Loop). Autrement dit cela va vous permettre de faire du Scala dans une console, mais aussi dans des scripts. Et cette REPL est extrêmement optimisée, donc votre machine ne va pas souffrir (lancement rapide, ...)

Remarque: si vous êtes sous Windows cela risque d'être un peu difficile à utiliser, mais certains y sont arrivés: https://github.com/lihaoyi/Ammonite/issues/119

Installation & 1er contact

Elle est très simple. Ouvrez un terminal et exécutez ceci:

$ sudo sh -c '(echo "#!/usr/bin/env sh" && curl -L https://github.com/lihaoyi/Ammonite/releases/download/1.1.2/2.12-1.1.2) > /usr/local/bin/amm && chmod +x /usr/local/bin/amm' && amm

Vous arriverez sur ceci:

Welcome to the Ammonite Repl 1.1.2
(Scala 2.12.6 Java 1.8.0_121)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi
@  

Vous pouvez commencer à saisir des commandes:

@ val res = 40 + 2 
res: Int = 42

@ res / 2 
res1: Int = 21

@ "hello 👋 world 🌍".startsWith("hello") 
res2: Boolean = true

@  
  • Pour "sortir", tapez exit et validez
  • Pour relancer, tapez amm et validez

Passons à quelque chose de plus amusant.

1er "script" Scala

Créez un fichier Hello.sc (⚠️ et pas .scala) avec ce contenu:

object Hello extends App {
  println("Hello World")
}

Hello.main(null)

Puis lancez:

amm --watch Hello.sc

Votre script va être compilé et exécuté:

Compiling (synthetic)/ammonite/predef/interpBridge.sc
Compiling (synthetic)/ammonite/predef/DefaultPredef.sc
Compiling /Users/k33g/Dropbox/BOTS.GARDEN/k33g/k33g.gitlab.io/sandbox/Hello.sc
Hello World
Watching for changes to 2 files... (Ctrl-C to exit)

et grace à l'option --watch, à chaque fois que vous allez modifier votre code, il sera automatiquement à nouveau compilé et exécuté (et plutôt rapidement).

Quelques astuces

Importez des fichiers

Créez un fichier animals.sc avec ce contenu:

class Animal(val name: String) {
  def hello() = { println(s"Hello I'm ${name}") }
}
class Dog(name: String) extends Animal(name) {
  def wouaf() = {
    println(s"Wouaf I'm ${name}")
  }
}

Pour pouvoir l'utiliser dans Hello.sc, utilisez import $file.animals

import $file.animals

object Hello extends App {
  val wolf = new animals.Dog("wolf")
  wolf.hello 
  wolf.wouaf 
}

Hello.main(null)

Vous devriez obtenir ceci:

Compiling /Users/k33g/Dropbox/BOTS.GARDEN/k33g/k33g.gitlab.io/sandbox/animals.sc
Compiling /Users/k33g/Dropbox/BOTS.GARDEN/k33g/k33g.gitlab.io/sandbox/Hello.sc
Hello I'm wolf
Wouaf I'm wolf
Watching for changes to 3 files... (Ctrl-C to exit)

La même chose mais dans un seul fichier

object animals {

  class Animal(val name: String) {
    def hello() = { println(s"Hello I'm ${name}") }
  }

  class Dog(name: String) extends Animal(name) {
    def wouaf() = {
      println(s"Wouaf I'm ${name}")
    }
  }
}

import animals._

object Hello extends App {
  val wolf = new Dog("wolf")
  wolf.hello 
  wolf.wouaf 
}

Hello.main(null)

Importer une librairie externe

Imaginons que vous vouliez faire du Vert-x avec Ammonite pour prototyper rapidement une webapp. Nous allons utiliser la commande import $ivy (équivalant de libraryDependencies += avc sbt). Quittez Ammonite. Puis, modifiez votre code de la manière suivante:

import $ivy.`io.vertx::vertx-web-scala:3.5.2`
//* libraryDependencies += "io.vertx" %% "vertx-web-scala" % "3.5.2"

import io.vertx.core.json.JsonObject
import io.vertx.scala.core.Vertx
import io.vertx.scala.ext.web.Router

import scala.util.{Try, Failure, Success}

object Hello extends App {
  val vertx = Vertx.vertx()
  val server = vertx.createHttpServer()
  val router = Router.router(vertx)

  val httpPort = sys.env.getOrElse("PORT", "9090").toInt

  router.get("/hey").handler(context => 
    context
      .response()
      .putHeader("content-type", "application/json;charset=UTF-8")
      .end(new JsonObject().put("message", "👋 hey!").encodePrettily())                            
  )

  println(s"🌍 Listening on $httpPort  - Enjoy 😄")
  server.requestHandler(router.accept).listen(httpPort)

  // trick for not exit from the application
  val waitForSomething = readLine("Type something ans valid to exit")
}

Hello.main(null)

Sauvegardez et lancez amm Hello.sc, la 1ère fois cela va télécharger les dépendances Vert-x, puis tentez un curl http://localhost:9090/hey, si tout va bien vous obtiendrez ce message:

{
  "message" : "👋 hey!"
}

Remarque: Dans ce cas là, --watch ne va pas fonctionner car vous aller vous retrouver avec une erreur SEVERE: java.net.BindException: Address already in use

Voilà, vous avez tout ce qu'il vous faut pour commencer à vous initier à Scala en toute tranquillité (avec un simple VSCode cela peut faire l'affaire). 😉

Last Articles

Last Updated: 7/14/2018, 8:44:10 AM