Quelles sont les fonctionnalités cachées de Scala dont chaque développeur Scala devrait être conscient?
Une fonction cachée par réponse, s'il vous plaît.
Quelles sont les fonctionnalités cachées de Scala dont chaque développeur Scala devrait être conscient?
Une fonction cachée par réponse, s'il vous plaît.
Réponses:
D'accord, j'ai dû en ajouter un de plus. Chaque Regex
objet dans Scala a un extracteur (voir la réponse d'oxbox_lakes ci-dessus) qui vous donne accès aux groupes de correspondance. Vous pouvez donc faire quelque chose comme:
// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"
La deuxième ligne semble déroutante si vous n'êtes pas habitué à utiliser la correspondance de motifs et les extracteurs. Chaque fois que vous définissez un val
ou var
, ce qui vient après le mot-clé n'est pas simplement un identifiant mais plutôt un modèle. C'est pourquoi cela fonctionne:
val (a, b, c) = (1, 3.14159, "Hello, world")
L'expression de droite crée un Tuple3[Int, Double, String]
qui peut correspondre au modèle (a, b, c)
.
La plupart du temps, vos modèles utilisent des extracteurs qui sont membres d'objets singleton. Par exemple, si vous écrivez un modèle comme
Some(value)
alors vous appelez implicitement l'extracteur Some.unapply
.
Mais vous pouvez également utiliser des instances de classe dans des modèles, et c'est ce qui se passe ici. Le val regex est une instance de Regex
, et lorsque vous l'utilisez dans un modèle, vous appelez implicitement regex.unapplySeq
( unapply
par opposition à ce qui unapplySeq
dépasse la portée de cette réponse), ce qui extrait les groupes de correspondance en a Seq[String]
, dont les éléments sont affectés afin de les variables année, mois et jour.
Définitions de type structurel - c'est-à-dire un type décrit par les méthodes qu'il prend en charge. Par exemple:
object Closer {
def using(closeable: { def close(): Unit }, f: => Unit) {
try {
f
} finally { closeable.close }
}
}
Notez que le type du paramètre closeable
n'est défini que s'il a une close
méthode
Sans cette fonctionnalité, vous pouvez, par exemple, exprimer l'idée de mapper une fonction sur une liste pour renvoyer une autre liste, ou de mapper une fonction sur un arbre pour renvoyer un autre arbre. Mais vous ne pouvez pas exprimer cette idée de manière générale sans types supérieurs.
Avec les types supérieurs, vous pouvez capturer l'idée de tout type paramétré avec un autre type. Un constructeur de type qui prend un paramètre est dit de type (*->*)
. Par exemple List
,. Un constructeur de type qui renvoie un autre constructeur de type est dit de type (*->*->*)
. Par exemple Function1
,. Mais dans Scala, nous avons des types supérieurs , nous pouvons donc avoir des constructeurs de type paramétrés avec d'autres constructeurs de type. Donc, ils sont du genre ((*->*)->*)
.
Par exemple:
trait Functor[F[_]] {
def fmap[A, B](f: A => B, fa: F[A]): F[B]
}
Maintenant, si vous avez un Functor[List]
, vous pouvez mapper sur des listes. Si vous avez un Functor[Tree]
, vous pouvez cartographier les arbres. Mais plus important encore, si vous avez Functor[A]
pour tout type A(*->*)
, vous pouvez mapper une fonction A
.
Extracteurs qui vous permettent de remplacer le if-elseif-else
code de style désordonné par des modèles. Je sais que ce ne sont pas exactement cachés mais j'utilise Scala depuis quelques mois sans vraiment en comprendre la puissance. Pour un (long) exemple, je peux remplacer:
val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
//e.g. GBP20090625.FWD
p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
p = ps.lookupProductByRic(code)
}
Avec cela, ce qui est beaucoup plus clair à mon avis
implicit val ps: ProductService = ...
val p = code match {
case SyntheticCodes.Cash(c) => c
case SyntheticCodes.Forward(f) => f
case _ => ps.lookupProductByRic(code)
}
Je dois faire un peu de travail en arrière-plan ...
object SyntheticCodes {
// Synthetic Code for a CashProduct
object Cash extends (CashProduct => String) {
def apply(p: CashProduct) = p.currency.name + "="
//EXTRACTOR
def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
if (s.endsWith("=")
Some(ps.findCash(s.substring(0,3)))
else None
}
}
//Synthetic Code for a ForwardProduct
object Forward extends (ForwardProduct => String) {
def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"
//EXTRACTOR
def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
if (s.endsWith(".FWD")
Some(ps.findForward(s.substring(0,3), s.substring(3, 9))
else None
}
}
Mais les démarches en valent la peine, car elles séparent un élément de logique métier en un endroit sensible. Je peux implémenter mes Product.getCode
méthodes comme suit.
class CashProduct {
def getCode = SyntheticCodes.Cash(this)
}
class ForwardProduct {
def getCode = SyntheticCodes.Forward(this)
}
Manifeste qui est une sorte de moyen d'obtenir les informations de type au moment de l'exécution, comme si Scala avait réifié des types.
Dans scala 2.8, vous pouvez avoir des méthodes récursives de fin en utilisant le package scala.util.control.TailCalls (en fait, il s'agit de trampoline).
Un exemple:
def u(n:Int):TailRec[Int] = {
if (n==0) done(1)
else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
if (n==0) done(5)
else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)
Les classes de cas se mélangent automatiquement dans le trait Product, fournissant un accès non typé et indexé aux champs sans aucune réflexion:
case class Person(name: String, age: Int)
val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)
Cette fonctionnalité fournit également un moyen simplifié de modifier la sortie de la toString
méthode:
case class Person(name: String, age: Int) {
override def productPrefix = "person: "
}
// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28))
Ce n'est pas exactement caché, mais certainement une fonctionnalité sous-annoncée: scalac -Xprint .
Pour illustrer l'utilisation, considérons la source suivante:
class A { "xx".r }
Compiler cela avec scalac -Xprint: sorties typer :
package <empty> {
class A extends java.lang.Object with ScalaObject {
def this(): A = {
A.super.this();
()
};
scala.this.Predef.augmentString("xx").r
}
}
Remarquez scala.this.Predef.augmentString("xx").r
, qui est une application du implicit def augmentString
présent dans Predef.scala.
scalac -Xprint: <phase> affichera l'arbre de syntaxe après une phase de compilation. Pour voir les phases disponibles, utilisez scalac -Xshow-phases .
C'est un excellent moyen d'apprendre ce qui se passe dans les coulisses.
Essayez avec
case class X(a:Int,b:String)
en utilisant la phase de frappe pour vraiment sentir à quel point c'est utile.
Vous pouvez définir vos propres structures de contrôle. Ce ne sont vraiment que des fonctions et des objets et du sucre syntaxique, mais ils ressemblent et se comportent comme la vraie chose.
Par exemple, le code suivant définit dont {...} unless (cond)
et dont {...} until (cond)
:
def dont(code: => Unit) = new DontCommand(code)
class DontCommand(code: => Unit) {
def unless(condition: => Boolean) =
if (condition) code
def until(condition: => Boolean) = {
while (!condition) {}
code
}
}
Vous pouvez maintenant effectuer les opérations suivantes:
/* This will only get executed if the condition is true */
dont {
println("Yep, 2 really is greater than 1.")
} unless (2 > 1)
/* Just a helper function */
var number = 0;
def nextNumber() = {
number += 1
println(number)
number
}
/* This will not be printed until the condition is met. */
dont {
println("Done counting to 5!")
} until (nextNumber() == 5)
zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero
. Nécessite Scalaz.
@switch
annotation dans Scala 2.8:
Une annotation à appliquer à une expression de correspondance. S'il est présent, le compilateur vérifiera que la correspondance a été compilée vers un commutateur de table ou un commutateur de recherche, et émettra une erreur s'il se compile à la place en une série d'expressions conditionnelles.
Exemple:
scala> val n = 3
n: Int = 3
scala> import annotation.switch
import annotation.switch
scala> val s = (n: @switch) match {
| case 3 => "Three"
| case _ => "NoThree"
| }
<console>:6: error: could not emit switch for @switch annotated match
val s = (n: @switch) match {
Je ne sais pas si cela est vraiment caché, mais je trouve cela plutôt sympa.
Les constructeurs de types qui prennent 2 paramètres de type peuvent être écrits en notation infixe
object Main {
class FooBar[A, B]
def main(args: Array[String]): Unit = {
var x: FooBar[Int, BigInt] = null
var y: Int FooBar BigInt = null
}
}
var foo2barConverter: Foo ConvertTo Bar
, l'ordre des paramètres de type est évident.
Scala 2.8 a introduit des arguments par défaut et nommés, ce qui a rendu possible l'ajout d'une nouvelle méthode "copie" que Scala ajoute aux classes de cas. Si vous définissez ceci:
case class Foo(a: Int, b: Int, c: Int, ... z:Int)
et vous voulez créer un nouveau Foo qui ressemble à un Foo existant, uniquement avec une valeur "n" différente, alors vous pouvez simplement dire:
foo.copy(n = 3)
dans scala 2.8, vous pouvez ajouter @specialized à vos classes / méthodes génériques. Cela créera des versions spéciales de la classe pour les types primitifs (étendant AnyVal) et économisera le coût d'un boxing / unboxing non nécessaire:
class Foo[@specialized T]...
Vous pouvez sélectionner un sous-ensemble d'AnyVals:
class Foo[@specialized(Int,Boolean) T]...
Extension de la langue. J'ai toujours voulu faire quelque chose comme ça en Java (impossible). Mais dans Scala, je peux avoir:
def timed[T](thunk: => T) = {
val t1 = System.nanoTime
val ret = thunk
val time = System.nanoTime - t1
println("Executed in: " + time/1000000.0 + " millisec")
ret
}
puis écrivez:
val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed { // "timed" is a new "keyword"!
numbers.sortWith(_<_)
}
println(sorted)
et obtenir
Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
Vous pouvez désigner un paramètre call-by-name (EDITED: c'est différent d'un paramètre paresseux!) À une fonction et il ne sera évalué qu'après utilisation par la fonction (EDIT: en fait, il sera réévalué à chaque fois utilisé). Consultez cette FAQ pour plus de détails
class Bar(i:Int) {
println("constructing bar " + i)
override def toString():String = {
"bar with value: " + i
}
}
// NOTE the => in the method declaration. It indicates a lazy paramter
def foo(x: => Bar) = {
println("foo called")
println("bar: " + x)
}
foo(new Bar(22))
/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/
lazy val xx: Bar = x
dans votre méthode et à partir de ce moment, vous n'utilisez que xx
.
Vous pouvez utiliser locally
pour introduire un bloc local sans causer de problèmes d'inférence de point-virgule.
Usage:
scala> case class Dog(name: String) {
| def bark() {
| println("Bow Vow")
| }
| }
defined class Dog
scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)
scala> locally {
| import d._
| bark()
| bark()
| }
Bow Vow
Bow Vow
locally
est défini dans "Predef.scala" comme:
@inline def locally[T](x: T): T = x
Étant en ligne, il n'impose aucune surcharge supplémentaire.
trait AbstractT2 {
println("In AbstractT2:")
val value: Int
val inverse = 1.0/value
println("AbstractT2: value = "+value+", inverse = "+inverse)
}
val c2c = new {
// Only initializations are allowed in pre-init. blocks.
// println("In c2c:")
val value = 10
} with AbstractT2
println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)
Production:
In AbstractT2:
AbstractT2: value = 10, inverse = 0.1
c2c.value = 10, inverse = 0.1
Nous instancions une classe interne anonyme, initialisant le
value
champ dans le bloc, avant lawith AbstractT2
clause. Cela garantit qu'ilvalue
est initialisé avant que le corps deAbstractT2
soit exécuté, comme indiqué lorsque vous exécutez le script.
Vous pouvez composer des types structurels avec le mot-clé "avec"
object Main {
type A = {def foo: Unit}
type B = {def bar: Unit}
type C = A with B
class myA {
def foo: Unit = println("myA.foo")
}
class myB {
def bar: Unit = println("myB.bar")
}
class myC extends myB {
def foo: Unit = println("myC.foo")
}
def main(args: Array[String]): Unit = {
val a: A = new myA
a.foo
val b: C = new myC
b.bar
b.foo
}
}
syntaxe d'espace réservé pour les fonctions anonymes
À partir de la spécification du langage Scala:
SimpleExpr1 ::= '_'
Une expression (de catégorie syntaxique
Expr
) peut contenir des symboles de soulignement incorporés_
aux endroits où les identificateurs sont légaux. Une telle expression représente une fonction anonyme où les occurrences ultérieures de traits de soulignement dénotent des paramètres successifs.
À partir des changements de langue Scala :
_ + 1 x => x + 1
_ * _ (x1, x2) => x1 * x2
(_: Int) * 2 (x: Int) => x * 2
if (_) x else y z => if (z) x else y
_.map(f) x => x.map(f)
_.map(_ + 1) x => x.map(y => y + 1)
En utilisant cela, vous pouvez faire quelque chose comme:
def filesEnding(query: String) =
filesMatching(_.endsWith(query))
Définitions implicites, en particulier les conversions.
Par exemple, supposons une fonction qui formatera une chaîne d'entrée pour l'adapter à une taille, en remplaçant le milieu de celle-ci par "...":
def sizeBoundedString(s: String, n: Int): String = {
if (n < 5 && n < s.length) throw new IllegalArgumentException
if (s.length > n) {
val trailLength = ((n - 3) / 2) min 3
val headLength = n - 3 - trailLength
s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
} else s
}
Vous pouvez l'utiliser avec n'importe quelle chaîne et, bien sûr, utiliser la méthode toString pour convertir n'importe quoi. Mais vous pouvez aussi l'écrire comme ceci:
def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
if (n < 5 && n < s.length) throw new IllegalArgumentException
if (s.length > n) {
val trailLength = ((n - 3) / 2) min 3
val headLength = n - 3 - trailLength
s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
} else s
}
Et puis, vous pouvez passer des classes d'autres types en faisant ceci:
implicit def double2String(d: Double) = d.toString
Vous pouvez maintenant appeler cette fonction en passant un double:
sizeBoundedString(12345.12345D, 8)
Le dernier argument est implicite et est passé automatiquement en raison de la déclaration implicite de. En outre, "s" est traité comme une chaîne à l'intérieur de sizeBoundedString car il y a une conversion implicite de celui-ci en String.
Les implicits de ce type sont mieux définis pour les types inhabituels afin d'éviter les conversions inattendues. Vous pouvez également passer explicitement une conversion, et elle sera toujours implicitement utilisée dans sizeBoundedString:
sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)
Vous pouvez également avoir plusieurs arguments implicites, mais vous devez alors les passer tous ou ne pas les transmettre. Il existe également une syntaxe de raccourci pour les conversions implicites:
def sizeBoundedString[T <% String](s: T, n: Int): String = {
if (n < 5 && n < s.length) throw new IllegalArgumentException
if (s.length > n) {
val trailLength = ((n - 3) / 2) min 3
val headLength = n - 3 - trailLength
s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
} else s
}
Ceci est utilisé exactement de la même manière.
Les implicits peuvent avoir n'importe quelle valeur. Ils peuvent être utilisés, par exemple, pour masquer les informations de la bibliothèque. Prenons l'exemple suivant, par exemple:
case class Daemon(name: String) {
def log(msg: String) = println(name+": "+msg)
}
object DefaultDaemon extends Daemon("Default")
trait Logger {
private var logd: Option[Daemon] = None
implicit def daemon: Daemon = logd getOrElse DefaultDaemon
def logTo(daemon: Daemon) =
if (logd == None) logd = Some(daemon)
else throw new IllegalArgumentException
def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}
class X extends Logger {
logTo(Daemon("X Daemon"))
def f = {
log("f called")
println("Stuff")
}
def g = {
log("g called")(DefaultDaemon)
}
}
class Y extends Logger {
def f = {
log("f called")
println("Stuff")
}
}
Dans cet exemple, appeler "f" dans un objet Y enverra le journal au démon par défaut, et sur une instance de X au démon Daemon X. Mais appeler g sur une instance de X enverra le journal au DefaultDaemon explicitement donné.
Bien que cet exemple simple puisse être réécrit avec une surcharge et un état privé, les implicits ne nécessitent pas d'état privé et peuvent être mis en contexte avec les importations.
Peut-être pas trop caché, mais je pense que c'est utile:
@scala.reflect.BeanProperty
var firstName:String = _
Cela générera automatiquement un getter et un setter pour le champ qui correspond à la convention du bean.
Description supplémentaire chez developerworks
Arguments implicites dans les fermetures.
Un argument de fonction peut être marqué comme implicite comme avec les méthodes. Dans le cadre du corps de la fonction, le paramètre implicite est visible et éligible pour une résolution implicite:
trait Foo { def bar }
trait Base {
def callBar(implicit foo: Foo) = foo.bar
}
object Test extends Base {
val f: Foo => Unit = { implicit foo =>
callBar
}
def test = f(new Foo {
def bar = println("Hello")
})
}
Construisez des structures de données infinies avec les Scala Stream
:
http://www.codecommit.com/blog/scala/infinite-lists-for-the-finately-patient
Les types de résultats dépendent de la résolution implicite. Cela peut vous donner une forme d'envoi multiple:
scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc
scala> implicit val stringToInt = new PerformFunc[String,Int] {
def perform(a : String) = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137
scala> implicit val intToDouble = new PerformFunc[Int,Double] {
def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4
scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B
scala> foo("HAI")
res16: Int = 5
scala> foo(1)
res17: Double = 1.0
foo
usages et a
qui doivent avoir été présents dans l'environnement avant l'exécution de ces commandes. Je suppose que vous vouliez dire z.perform(x)
.
Scala vous permet de créer une sous-classe anonyme avec le corps de la classe (le constructeur) contenant des instructions pour initialiser l'instance de cette classe.
Ce modèle est très utile lors de la construction d'interfaces utilisateur basées sur des composants (par exemple Swing, Vaadin) car il permet de créer des composants d'interface utilisateur et de déclarer leurs propriétés de manière plus concise.
Voir http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf pour plus d'informations.
Voici un exemple de création d'un bouton Vaadin:
val button = new Button("Click me"){
setWidth("20px")
setDescription("Click on this")
setIcon(new ThemeResource("icons/ok.png"))
}
import
déclarationsSupposons que vous souhaitiez utiliser une Logger
qui contient une méthode println
et une printerr
, mais que vous ne souhaitiez utiliser que celle pour les messages d'erreur et conserver la bonne vieille Predef.println
pour la sortie standard. Vous pouvez faire ceci:
val logger = new Logger(...)
import logger.printerr
mais si logger
contient également douze autres méthodes que vous souhaitez importer et utiliser, il devient peu pratique de les lister. Vous pouvez à la place essayer:
import logger.{println => donotuseprintlnt, _}
mais cela «pollue» toujours la liste des membres importés. Entrez le joker über-puissant:
import logger.{println => _, _}
et cela fera ce qu'il faut ™.
require
méthode (définie dans Predef
) qui vous permet de définir des contraintes de fonction supplémentaires qui seraient vérifiées lors de l'exécution. Imaginez que vous développiez un autre client Twitter et que vous deviez limiter la longueur des tweets à 140 symboles. De plus, vous ne pouvez pas publier de tweet vide.
def post(tweet: String) = {
require(tweet.length < 140 && tweet.length > 0)
println(tweet)
}
Maintenant, appeler post avec un argument de longueur inapproprié provoquera une exception:
scala> post("that's ok")
that's ok
scala> post("")
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:145)
at .post(<console>:8)
scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet")
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:145)
at .post(<console>:8)
Vous pouvez rédiger plusieurs exigences ou même ajouter une description à chacune:
def post(tweet: String) = {
require(tweet.length > 0, "too short message")
require(tweet.length < 140, "too long message")
println(tweet)
}
Maintenant, les exceptions sont verbeuses:
scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
at scala.Predef$.require(Predef.scala:157)
at .post(<console>:8)
Un autre exemple est ici .
Vous pouvez effectuer une action chaque fois que l'exigence échoue:
scala> var errorcount = 0
errorcount: Int = 0
def post(tweet: String) = {
require(tweet.length > 0, {errorcount+=1})
println(tweet)
}
scala> errorcount
res14: Int = 0
scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
at scala.Predef$.require(Predef.scala:157)
at .post(<console>:9)
...
scala> errorcount
res16: Int = 1
require
n'est pas un mot réservé. Ce n'est qu'une méthode définie dans Predef
.
Les traits avec des abstract override
méthodes sont une fonctionnalité de Scala qui n'est pas aussi largement annoncée que beaucoup d'autres. Le but des méthodes avec le abstract override
modificateur est d'effectuer certaines opérations et de déléguer l'appel à super
. Ensuite, ces traits doivent être mélangés avec des implémentations concrètes de leurs abstract override
méthodes.
trait A {
def a(s : String) : String
}
trait TimingA extends A {
abstract override def a(s : String) = {
val start = System.currentTimeMillis
val result = super.a(s)
val dur = System.currentTimeMillis-start
println("Executed a in %s ms".format(dur))
result
}
}
trait ParameterPrintingA extends A {
abstract override def a(s : String) = {
println("Called a with s=%s".format(s))
super.a(s)
}
}
trait ImplementingA extends A {
def a(s: String) = s.reverse
}
scala> val a = new ImplementingA with TimingA with ParameterPrintingA
scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a
Bien que mon exemple ne soit vraiment pas beaucoup plus qu'un AOP pour homme pauvre, j'ai utilisé ces traits empilables à mon goût pour créer des instances d'interpréteur Scala avec des importations prédéfinies, des liaisons personnalisées et des chemins de classe. Les traits empilables ont permis de créer mon usine le long des lignes de new InterpreterFactory with JsonLibs with LuceneLibs
puis d'avoir des importations et des variables de portée utiles pour les scripts des utilisateurs.