SlideShare une entreprise Scribd logo
1  sur  91
Télécharger pour lire hors ligne
©ASERT2006-2013
Dr Paul King
@paulk_asert
http:/slideshare.net/paulk_asert/groovy-rules
https://github.com/paulk-asert/groovy-rules
Leveraging Groovy for
Capturing Business Rules
Topics
Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
©ASERT2006-2013
What is a DSL?
• A domain-specific language is a
programming language or executable
specification language that offers, through
appropriate notations and abstractions,
expressive power focused on, and usually
restricted to, a particular problem domain
– declarative data << DSL << general-purpose programming
language (GPL)
– AKA: fluent / human interfaces, language oriented
programming, problem-oriented languages, little / mini
languages, macros, business natural languages
Sources:
http://en.wikipedia.org/wiki/Domain-specific_language
van Deursen, A., Klint, P., Visser, J.: Domain-specific languages: an annotated bibliography. ACM SIGPLAN Notices 35 (2000) 26–36
©ASERT2006-2013
Goals of DSLs
• Use a more expressive language than a
general-purpose one
• Share a common metaphor of
understanding between developers and
subject matter experts
• Have domain experts help with the design
of the business logic of an application
• Avoid cluttering business code with
boilerplate technical code thanks to a
clean separation
• Let business rules have their own lifecycle
Why Scripting DSLs
package org.drools.examples.golfing;
dialect "mvel"
import org.drools.examples.golfing.GolfingExample.Golfer;
rule "find solution"
when
// Bob is wearing plaid pants
$bob : Golfer( name == "Bob", color == "plaid")
// ...
then
System.out.println( "Bob " + $bob.getColor() );
end
package org.drools.examples.golfing;
dialect "mvel"
import org.drools.examples.golfing.GolfingExample.Golfer;
rule "find solution"
when
Bob is wearing plaid pants
// ...
then
Display all details
end
when Bob is wearing plaid pants display all details
Compile time
translation
Compile time
or runtime
translation
Why a DSL?
• Advantages:
– Domain experts can
understand, validate,
modify, and often even
develop DSL programs
– Self-documenting (?)
– Enhance quality,
productivity, reliability,
maintainability, portability
and reusability
– Safety; language
constructs can be made
safe
– Tooling
• Disadvantages:
– Learning cost vs. limited
applicability
– Cost of designing,
implementing &
maintaining DSL & tools
– Attaining proper scope
– Trade-offs between DSL
specific and general-
purpose programming
language constructs
– Efficiency costs
– Proliferation of similar
non-standard DSLs
– Tooling
©ASERT2006-2013
Topics
• Introduction to DSLs
Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
©ASERT2006-2013
Java code for list manipulation
©ASERT2006-2013
import java.util.List;
import java.util.ArrayList;
class Main {
private List keepShorterThan(List strings, int length) {
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
String s = (String) strings.get(i);
if (s.length() < length) {
result.add(s);
}
}
return result;
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Main main = new Main();
List shortNames = main.keepShorterThan(names, 4);
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
Based on an
example by
Jim Weirich
& Ted Leung
Groovy code for list manipulation
©ASERT2006-2013
import java.util.List;
import java.util.ArrayList;
class Main {
private List keepShorterThan(List strings, int length) {
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
String s = (String) strings.get(i);
if (s.length() < length) {
result.add(s);
}
}
return result;
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Main main = new Main();
List shortNames = main.keepShorterThan(names, 4);
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
Rename
Main.java
to
Main.groovy
Some Java Boilerplate identified
©ASERT2006-2013
import java.util.List;
import java.util.ArrayList;
class Main {
private List keepShorterThan(List strings, int length) {
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
String s = (String) strings.get(i);
if (s.length() < length) {
result.add(s);
}
}
return result;
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Main main = new Main();
List shortNames = main.keepShorterThan(names, 4);
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
Are the semicolons
needed?
And shouldn’t
we us more
modern list
notation?
Why not
import common
libraries?
Do we need
the static types?
Must we always
have a main
method and
class definition?
How about
improved
consistency?
Java Boilerplate removed
©ASERT2006-2013
def keepShorterThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() < length) {
result.add(s)
}
}
return result
}
names = new ArrayList()
names.add("Ted"); names.add("Fred")
names.add("Jed"); names.add("Ned")
System.out.println(names)
shortNames = keepShorterThan(names, 4)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
More Java Boilerplate identified
©ASERT2006-2013
def keepShorterThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() < length) {
result.add(s)
}
}
return result
}
names = new ArrayList()
names.add("Ted"); names.add("Fred")
names.add("Jed"); names.add("Ned")
System.out.println(names)
shortNames = keepShorterThan(names, 4)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
Shouldn’t we
have special
notation for lists?
And special
facilities for
list processing?
Is ‘return’
needed at end?
Is the method
now needed?
Simplify common
methods?
Remove unambiguous
brackets?
Boilerplate removed = nicer Groovy version
©ASERT2006-2013
names = ["Ted", "Fred", "Jed", "Ned"]
println names
shortNames = names.findAll{ it.size() < 4 }
println shortNames.size()
shortNames.each{ println it }
["Ted", "Fred", "Jed", "Ned"]
3
Ted
Jed
Ned
Output:
Or Groovy DSL version if required
©ASERT2006-2013
names = []
def of, having, less = null
def given(_the) { [names:{ Object[] ns -> names.addAll(ns)
[and: { n -> names += n }] }] }
def the = [
number: { _of -> [names: { _having -> [size: { _less -> [than: { size ->
println names.findAll{ it.size() < size }.size() }]}] }] },
names: { _having -> [size: { _less -> [than: { size ->
names.findAll{ it.size() < size }.each{ println it } }]}] }
]
def all = [
the: { println names }
]
def display(arg) { arg }
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
Closures
©ASERT2006-2013
int twice(int arg) {
arg * 2
}
def triple = { int arg -> arg * 3 }
println twice(3) // => 6
println triple(3) // => 9
Grapes / Grab: Google collections
©ASERT2006-2013
@Grab('com.google.guava:guava:r09')
import com.google.common.collect.HashBiMap
HashBiMap fruit =
[grape:'purple', lemon:'yellow', lime:'green']
assert fruit.lemon == 'yellow'
assert fruit.inverse().yellow == 'lemon'
©ASERT2006-2011
Groovy Builders
<html>
<head>
<title>Hello</title>
</head>
<body>
<ul>
<li>world 1</li>
<li>world 2</li>
<li>world 3</li>
<li>world 4</li>
<li>world 5</li>
</ul>
</body>
</html>
import groovy.xml.*
def page = new MarkupBuilder()
page.html {
head { title 'Hello' }
body {
ul {
for (count in 1..5) {
li "world $count"
}
}
}
}
• Markup Builder
Topics
• Introduction to DSLs
• Introduction to Groovy
DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
©ASERT2006-2013
DSL example...
©ASERT2006-2013
class FluentApi {
def action, what
def the(what) { this.what = what; this }
def of(arg) { action(what(arg)) }
}
show = { arg -> println arg }
square_root = { Math.sqrt(it) }
please = { new FluentApi(action: it) }
please show the square_root of 100 // => 10.0
…DSL example...
©ASERT2006-2013
class FluentApi {
def action, what
def the(what) { this.what = what; this }
def of(arg) { action(what(arg)) }
}
show = { arg -> println arg }
square_root = { Math.sqrt(it) }
please = { new FluentApi(action: it) }
please show the square_root of 100 // => 10.0
DSL implementation details
…DSL example...
©ASERT2006-2013
class FluentApi {
def action, what
def the(what) { this.what = what; this }
def of(arg) { action(what(arg)) }
}
show = { arg -> println arg }
square_root = { Math.sqrt(it) }
please = { new FluentApi(action: it) }
please show the square_root of 100 // => 10.0
DSL usage
…DSL example...
©ASERT2006-2013
please show the square_root of 100
…DSL example...
©ASERT2006-2013
please show the square_root of 100
please(show).the(square_root).of(100)
…DSL example...
©ASERT2006-2013
class FluentApi {
def action, what
def the(what) { this.what = what; this }
def of(arg) { action(what(arg)) }
}
show = { arg -> println arg }
square_root = { Math.sqrt(it) }
please = { new FluentApi(action: it) }
please(show).the(square_root).of(100)
…DSL example...
©ASERT2006-2013
Object.metaClass.please =
{ clos -> clos(delegate) }
Object.metaClass.the =
{ clos -> delegate[1](clos(delegate[0])) }
show = { thing -> [thing, { println it }] }
square_root = { Math.sqrt(it) }
given = { it }
given 100 please show the square_root
// ==> 10.0
...DSL example...
©ASERT2006-2013
show = { println it }
square_root = { Math.sqrt(it) }
def please(action) {
[the: { what ->
[of: { n -> action(what(n)) }]
}]
}
please show the square_root of 100
// ==> 10.0
...DSL example...
©ASERT2006-2013
show = { println it }
square_root = { Math.sqrt(it) }
def please(action) {
[the: { what ->
[of: { n -> action(what(n)) }]
}]
}
please show the square_root of 100
// ==> 10.0
Inspiration for this example came from …
...DSL example
©ASERT2006-2013
// Japanese DSL using GEP3 rules
Object.metaClass.を =
Object.metaClass.の =
{ clos -> clos(delegate) }
まず = { it }
表示する = { println it }
平方根 = { Math.sqrt(it) }
まず 100 の 平方根 を 表示する
// First, show the square root of 100
// => 10.0
source: http://d.hatena.ne.jp/uehaj/20100919/1284906117
also: http://groovyconsole.appspot.com/edit/241001
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
©ASERT2006-2013
Groovy provides
• A flexible and malleable syntax
– scripts, native syntax constructs (list, map,
ranges)
• Closures, less punctuation...
– Compile-time and runtime meta-programming
– metaclasses, AST transformations
– also operator overloading
• The ability to easily integrate into Java,
app’n server apps
– compile into bytecode or leave in source form
– also security and safety
Compile-time Metaprogramming
©ASERT2006-2013
Transformation
@Immutable...
• Java Immutable Class
– As per Joshua Bloch
Effective Java
©ASERT2006-2013
public final class Person {
private final String first;
private final String last;
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null)
? 0 : first.hashCode());
result = prime * result + ((last == null)
? 0 : last.hashCode());
return result;
}
public Person(String first, String last) {
this.first = first;
this.last = last;
}
// ...
// ...
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (last == null) {
if (other.last != null)
return false;
} else if (!last.equals(other.last))
return false;
return true;
}
@Override
public String toString() {
return "Person(first:" + first
+ ", last:" + last + ")";
}
}
...@Immutable...
• Java Immutable Class
– As per Joshua Bloch
Effective Java
©ASERT2006-2013
public final class Person {
private final String first;
private final String last;
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null)
? 0 : first.hashCode());
result = prime * result + ((last == null)
? 0 : last.hashCode());
return result;
}
public Person(String first, String last) {
this.first = first;
this.last = last;
}
// ...
// ...
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (last == null) {
if (other.last != null)
return false;
} else if (!last.equals(other.last))
return false;
return true;
}
@Override
public String toString() {
return "Person(first:" + first
+ ", last:" + last + ")";
}
}
boilerplate
...@Immutable
©ASERT2006-2013
@Immutable class Person {
String first, last
}
Rules engines
• Backward chaining starts with a list of goals (or a
hypothesis) and works backwards applying rules to
derive new hypotheses until available data is found
to support the hypotheses or all rules and data have
been exhausted
– http://en.wikipedia.org/wiki/Backward_chaining
• Forward chaining starts with the available facts and
applies rules to derive or infer more facts until a
goal is reached
– http://en.wikipedia.org/wiki/Forward_chaining
Backward chaining example
• Rules
– If X croaks and eats flies – Then X is a frog
– If X chirps and sings – Then X is a canary
– If X is a frog – Then X is green
– If X is a canary – Then X is yellow
• Facts
– Fritz croaks
– Fritz eats flies
– Tweety eats flies
– Tweety chirps
– Tweety is yellow
Who is a frog?
? is a frog
Based on rule 1, the computer can derive:
2. ? croaks and eats flies
Based on logic, the computer can derive:
3. ? croaks and ? eats flies
Based on the facts, the computer can derive:
4. Fritz croaks and Fritz eats flies
Forward chaining example
• Rules
– If X croaks and eats flies – Then X is a frog
– If X chirps and sings – Then X is a canary
– If X is a frog – Then X is green
– If X is a canary – Then X is yellow
• Facts
– Fritz croaks
– Fritz eats flies
– Tweety eats flies
– Tweety chirps
– Tweety is yellow
Who is a frog?
1. Fritz croaks and Fritz eats flies
Based on logic, the computer can derive:
2. Fritz croaks and eats flies
Based on rule 1, the computer can derive:
3. Fritz is a frog
Drools Expert Golfing Example…/*
* Copyright 2010 JBoss Inc
* Licensed under the Apache License…
*/
package org.drools.examples.golfing;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;
public class GolfingExample {
/**
* @param args
*/
public static void main(final String[] args) {
final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("golf.drl",
GolfingExample.class),
ResourceType.DRL);
final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
String[] names = new String[]{"Fred", "Joe", "Bob", "Tom"};
String[] colors = new String[]{"red", "blue", "plaid", "orange"};
int[] positions = new int[]{1, 2, 3, 4};
for (int n = 0; n < names.length; n++) {
for (int c = 0; c < colors.length; c++) {
for (int p = 0; p < positions.length; p++) {
ksession.insert(new Golfer(names[n],
colors[c],
positions[p]));
}
}
}
// ...
// ...
ksession.fireAllRules();
ksession.dispose();
}
public static class Golfer {
private String name;
private String color;
private int position;
public Golfer() { }
public Golfer(String name,
String color,
int position) {
super();
this.name = name;
this.color = color;
this.position = position;
}
/**
* @return the color
*/
public String getColor() {
return this.color;
}
/**
* @return the name
*/
public String getName() {
return this.name;
}
/**
* @return the name
*/
public int getPosition() {
return this.position;
}
}
}
…Drools Expert Golfing Example
import groovy.transform.Immutable
import static org.drools.builder.ResourceType.DRL
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newClassPathResource
def kbuilder = newKnowledgeBuilder()
kbuilder.add(newClassPathResource("golf.drl", getClass()), DRL)
def kbase = newKnowledgeBase()
kbase.addKnowledgePackages(kbuilder.knowledgePackages)
def ksession = kbase.newStatefulKnowledgeSession()
def names = ["Fred", "Joe", "Bob", "Tom"]
def colors = ["red", "blue", "plaid", "orange"]
def positions = [1, 2, 3, 4]
[names, colors, positions].combinations().each { n, c, p ->
ksession.insert(new Golfer(n, c, p))
}
ksession.fireAllRules()
ksession.dispose()
@Immutable
class Golfer {
String name
String color
int position
}
…Drools Expert Golfing Example
import groovy.transform.Immutable
import static org.drools.builder.ResourceType.DRL
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newClassPathResource
def kbuilder = newKnowledgeBuilder()
kbuilder.add(newClassPathResource("golf.drl", getClass()), DRL)
def kbase = newKnowledgeBase()
kbase.addKnowledgePackages(kbuilder.knowledgePackages)
def ksession = kbase.newStatefulKnowledgeSession()
def names = ["Fred", "Joe", "Bob", "Tom"]
def colors = ["red", "blue", "plaid", "orange"]
def positions = [1, 2, 3, 4]
[names, colors, positions].combinations().each { n, c, p ->
ksession.insert(new Golfer(n, c, p))
}
ksession.fireAllRules()
ksession.dispose()
@Immutable
class Golfer {
String name
String color
int position
}
…Drools Expert Golfing Example
import groovy.transform.Immutable
import static org.drools.builder.ResourceType.DRL
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newClassPathResource
def kbuilder = newKnowledgeBuilder()
kbuilder.add(newClassPathResource("golf.drl", getClass()), DRL)
def kbase = newKnowledgeBase()
kbase.addKnowledgePackages(kbuilder.knowledgePackages)
def ksession = kbase.newStatefulKnowledgeSession()
def names = ["Fred", "Joe", "Bob", "Tom"]
def colors = ["red", "blue", "plaid", "orange"]
def positions = [1, 2, 3, 4]
[names, colors, positions].combinations().each { n, c, p ->
ksession.insert(new Golfer(n, c, p))
}
ksession.fireAllRules()
ksession.dispose()
@Immutable
class Golfer {
String name
String color
int position
}
@Grab…
• Set up dependencies as per Java
– E.g. manually add to classpath or use
Maven/Gradle/Ant or rely on IDE features
• Or @Grab declares dependencies inline
– Makes scripts environment independent
– Downloads transient dependencies as needed
– (Uncomment in github examples)
//@GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/')
//@Grab('org.drools:drools-compiler:5.5.0.Final')
//@Grab('org.drools:drools-core:5.5.0.Final')
//@Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1;transitive=false')
//@Grab('com.google.protobuf:protobuf-java:2.4.1')
//@Grab('org.slf4j:slf4j-simple:1.6.4')
import groovy.transform.Immutable
import org.drools.builder.ResourceType
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newClassPathResource
def kbuilder = newKnowledgeBuilder()
// ...
…@Grab…
> groovy -classpath C:ProjectsGroovyProblemSolversoutproductionDroolsExpert;
C:Userspaulk.groovygrapesorg.droolsdrools-compilerjarsdrools-compiler-5.3.3.Final.jar;
C:Userspaulk.groovygrapesorg.antlrantlr-runtimejarsantlr-runtime-3.3.jar;
C:Userspaulk.groovygrapesorg.antlrantlrjarsantlr-3.3.jar;
C:Userspaulk.groovygrapesorg.antlrstringtemplatejarsstringtemplate-3.2.1.jar;
C:Userspaulk.groovygrapesantlrantlrjarsantlr-2.7.7.jar;
C:Userspaulk.groovygrapesorg.eclipse.jdt.core.compilerecjjarsecj-3.5.1.jar;
C:Userspaulk.groovygrapesorg.mvelmvel2jarsmvel2-2.1.0.drools16.jar;
C:Userspaulk.groovygrapescom.sun.xml.bindjaxb-xjcjarsjaxb-xjc-2.2.5.jboss-1.jar;
C:Userspaulk.groovygrapescom.sun.xml.bindjaxb-impljarsjaxb-impl-2.2.5.jboss-1.jar;
C:Userspaulk.groovygrapesjavax.xml.bindjaxb-apijarsjaxb-api-2.2.6.jar;
C:Userspaulk.groovygrapescom.sun.istackistack-commons-runtimejarsistack-commons-runtime-2.6.1.jar;
C:Userspaulk.groovygrapesjavax.xml.streamstax-apijarsstax-api-1.0-2.jar;
C:Userspaulk.groovygrapesjavax.activationactivationjarsactivation-1.1.jar;
C:Userspaulk.groovygrapescom.sun.xml.txw2txw2jarstxw2-20110809.jar;
C:Userspaulk.groovygrapesrelaxngDatatyperelaxngDatatypejarsrelaxngDatatype-20020414.jar;
C:Userspaulk.groovygrapescom.sun.codemodelcodemodeljarscodemodel-2.6.jar;
C:Userspaulk.groovygrapescom.sun.xml.dtd-parserdtd-parserjarsdtd-parser-1.1.jboss-1.jar;
C:Userspaulk.groovygrapescom.sun.istackistack-commons-toolsjarsistack-commons-tools-2.6.1.jar;
C:Userspaulk.groovygrapesorg.apache.antantjarsant-1.7.0.jar;
C:Userspaulk.groovygrapesorg.apache.antant-launcherjarsant-launcher-1.7.0.jar;
C:Userspaulk.groovygrapesorg.kohsuke.rngomrngomjarsrngom-201103.jboss-1.jar;
C:Userspaulk.groovygrapescom.sun.xsomxsomjarsxsom-20110809.jar;
C:Userspaulk.groovygrapesxml-resolverxml-resolverjarsxml-resolver-1.1.jar;
C:Userspaulk.groovygrapesorg.droolsdrools-corejarsdrools-core-5.3.3.Final.jar;
C:Userspaulk.groovygrapesorg.droolsknowledge-apijarsknowledge-api-5.3.3.Final.jar;
C:ProjectsGroovyProblemSolversDroolsExpertsrcresources GolfExample.groovy
Or you can precompile:
> groovyc -classpath ... GolfExample.groovy
> jar ...
Then use groovy or java commands to run.
Old school
…@Grab…
> groovy GolfExample.groovy
@GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/')
@Grab('org.drools:drools-compiler:5.5.0.Final')
@Grab('org.drools:drools-core:5.5.0.Final')
@Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1;transitive=false')
@Grab('com.google.protobuf:protobuf-java:2.4.1')
@Grab('org.slf4j:slf4j-simple:1.6.4')
import groovy.transform.Immutable
import org.drools.builder.ResourceType
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newClassPathResource
def kbuilder = newKnowledgeBuilder()
// ...
With @Grab
…@Grab
> groovy GolfExample
With @Grab
@GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/')
@Grab('org.drools:drools-compiler:5.5.0.Final')
@Grab('org.drools:drools-core:5.5.0.Final')
@Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1;transitive=false')
@Grab('com.google.protobuf:protobuf-java:2.4.1')
@Grab('org.slf4j:slf4j-simple:1.6.4')
import groovy.transform.Immutable
import org.drools.builder.ResourceType
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newClassPathResource
def kbuilder = newKnowledgeBuilder()
// ...
golf.drl…
dialect "mvel"
import Golfer;
rule "find solution"
when
// There is a golfer named Fred,
$fred : Golfer( name == "Fred" )
// Joe is in position 2
$joe : Golfer( name == "Joe",
position == 2,
position != $fred.position,
color != $fred.color )
// Bob is wearing plaid pants
$bob : Golfer( name == "Bob",
position != $fred.position,
position != $joe.position,
color == "plaid",
color != $fred.color,
color != $joe.color )
// ...
…golf.drl
// Tom isn't in position 1 or 4
// and isn't wearing orange
$tom : Golfer( name == "Tom",
position != 1,
position != 4,
position != $fred.position,
position != $joe.position,
position != $bob.position,
color != "orange",
color != $fred.color,
color != $joe.color,
color != $bob.color )
// The golfer to Fred's immediate right
// is wearing blue pants
Golfer( position == ( $fred.position + 1 ),
color == "blue",
this in ( $joe, $bob, $tom ) )
then
System.out.println("Fred " + $fred.getPosition() + " " + $fred.getColor());
System.out.println("Joe " + $joe.getPosition() + " " + $joe.getColor());
System.out.println("Bob " + $bob.getPosition() + " " + $bob.getColor());
System.out.println("Tom " + $tom.getPosition() + " " + $tom.getColor());
end
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
©ASERT2006-2013
Tortoises & Cranes
• Around a pond dwell tortoises and cranes
• There are 7 animals in total
• There are 20 legs in total
• How many of each animal are there?
Source: http://www.youtube.com/watch?v=tUs4olWQYS4
Tortoises & Cranes: Choco…
//@GrabResolver('http://www.emn.fr/z-info/choco-repo/mvn/repository')
//@Grab('choco:choco-solver:2.1.5')
import static choco.Choco.*
import choco.cp.model.CPModel
import choco.cp.solver.CPSolver
def m = new CPModel()
def s = new CPSolver()
def totalAnimals = 7
def totalLegs = 20
def c = makeIntVar('Cranes', 0, totalAnimals)
def t = makeIntVar('Tortoises', 0, totalAnimals)
m.addConstraint(eq(plus(c, t), totalAnimals))
m.addConstraint(eq(plus(mult(c, 2), mult(t, 4)), totalLegs))
s.read(m)
def more = s.solve()
while (more) {
println "Found a solution:"
[c, t].each {
def v = s.getVar(it)
if (v.val) println " $v.val * $v.name"
}
more = s.nextSolution()
}
Found a solution:
4 * Cranes
3 * Tortoises
…Tortoises & Cranes: Choco
import static choco.Choco.*
import choco.cp.model.CPModel
import choco.cp.solver.CPSolver
import choco.kernel.model.variables.integer.IntegerVariable
def m = new CPModel()
def s = new CPSolver()
def totalAnimals = 7
def totalLegs = 20
def c = makeIntVar('Cranes', 0, totalAnimals)
def t = makeIntVar('Tortoises', 0, totalAnimals)
IntegerVariable[] animals = [c, t]
m.addConstraint(eq(plus(c, t), totalAnimals))
m.addConstraint(eq(scalar(animals, [2, 4] as int[]), totalLegs))
s.read(m)
def more = s.solve()
while (more) {
println "Found a solution:"
animals.each {
def v = s.getVar(it)
if (v.val) println " $v.val * $v.name"
}
more = s.nextSolution()
}
Slight variant
using scalars.
Well suited to
scaling to
more animals
Tortoises & Cranes: Simplistic…
import groovy.transform.Immutable
import org.drools.builder.ResourceType
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newReaderResource
def numAnimals = 7
def numLegs = 20
def kbuilder = newKnowledgeBuilder()
kbuilder.add(newReaderResource(new StringReader('''
dialect "mvel"
rule "deduce animal counts"
when
$crane : Crane( )
$tortoise : Tortoise(
quantity + $crane.quantity == ''' + numAnimals + ''',
quantity * numLegs + $crane.quantity * $crane.numLegs == ''' + numLegs + '''
)
then
System.out.println( "Cranes " + $crane.getQuantity() )
System.out.println( "Tortoises " + $tortoise.getQuantity() )
end
''')), ResourceType.DRL)
def kbase = newKnowledgeBase()
kbase.addKnowledgePackages(kbuilder.knowledgePackages)
def ksession = kbase.newStatefulKnowledgeSession()
import groovy.transform.Immutable
import org.drools.builder.ResourceType
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newReaderResource
def numAnimals = 7
def numLegs = 20
def kbuilder = newKnowledgeBuilder()
kbuilder.add(newReaderResource(new StringReader('''
dialect "mvel"
rule "deduce animal counts"
when
$crane : Crane( )
$tortoise : Tortoise(
quantity + $crane.quantity == ''' + numAnimals + ''',
quantity * numLegs + $crane.quantity * $crane.numLegs == ''' + numLegs + '''
)
then
System.out.println( "Cranes " + $crane.getQuantity() )
System.out.println( "Tortoises " + $tortoise.getQuantity() )
end
''')), ResourceType.DRL)
def kbase = newKnowledgeBase()
kbase.addKnowledgePackages(kbuilder.knowledgePackages)
def ksession = kbase.newStatefulKnowledgeSession()
…Tortoises & Cranes: Simplistic…
…
(numAnimals + 1).times { n ->
if (numLegs.intdiv(Crane.numLegs) >= n) {
ksession.insert(new Crane(n))
}
if (numLegs.intdiv(Tortoise.numLegs) >= n) {
ksession.insert(new Tortoise(n))
}
}
ksession.fireAllRules()
ksession.dispose()
@Immutable
class Crane {
static int numLegs = 2
int quantity
}
@Immutable
class Tortoise {
static int numLegs = 4
int quantity
}
import groovy.transform.Immutable
import org.drools.builder.ResourceType
import static org.drools.KnowledgeBaseFactory.newKnowledgeBase
import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder
import static org.drools.io.ResourceFactory.newReaderResource
def numAnimals = 7
def numLegs = 20
def kbuilder = newKnowledgeBuilder()
kbuilder.add(newReaderResource(new StringReader('''
dialect "mvel"
rule "deduce animal counts"
when
$crane : Crane( )
$tortoise : Tortoise(
quantity + $crane.quantity == ''' + numAnimals + ''',
quantity * numLegs + $crane.quantity * $crane.numLegs == ''' + numLegs + '''
)
then
System.out.println( "Cranes " + $crane.getQuantity() )
System.out.println( "Tortoises " + $tortoise.getQuantity() )
end
''')), ResourceType.DRL)
def kbase = newKnowledgeBase()
kbase.addKnowledgePackages(kbuilder.knowledgePackages)
def ksession = kbase.newStatefulKnowledgeSession()
…Tortoises & Cranes: Simplistic
…
(numAnimals + 1).times { n ->
if (numLegs.intdiv(Crane.numLegs) >= n) {
ksession.insert(new Crane(n))
}
if (numLegs.intdiv(Tortoise.numLegs) >= n) {
ksession.insert(new Tortoise(n))
}
}
ksession.fireAllRules()
ksession.dispose()
@Immutable
class Crane {
static int numLegs = 2
int quantity
}
@Immutable
class Tortoise {
static int numLegs = 4
int quantity
}
What is the impact
of adding another
kind of animal?
Tortoises & Cranes: DSL…
//@GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/')
//@Grab('org.drools:knowledge-api:5.4.0.Final')
//@Grab('org.drools:drools-compiler:5.4.0.Final')
//@Grab('org.drools:drools-core:5.4.0.Final')
//@Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1')
//@GrabExclude('com.github.relaxng:relaxngDatatype')
import groovy.transform.Field
import org.drools.builder.ResourceType
import static org.drools.KnowledgeBaseFactory.*
import static org.drools.builder.KnowledgeBuilderFactory.*
import static org.drools.io.ResourceFactory.newReaderResource
class Solver {
static main(Map animals, int totalAnimals, int totalLegs, ClassLoader loader) {
def whenClauses = ''
def thenClauses = ''
def numAnimalsClause = ''
def numLegsClause = ''
def lastIndex = animals.size() - 1
animals.eachWithIndex { entry, index ->
def key = entry.key
def capKey = key.capitalize()
whenClauses += ' $' + "$key : $capKey ("
thenClauses += " System.out.println( "$capKey "" + ' + $' + key + '.getQuantity() )n'
if (index != lastIndex) {
numAnimalsClause += ' + $' + key + '.quantity'
numLegsClause += ' + $' + key + '.quantity * $' + key + '.numLegs'
whenClauses += ' )n'
} else {
whenClauses += 'n quantity' + numAnimalsClause + ' == ' + totalAnimals + ','
whenClauses += 'n quantity * numLegs' + numLegsClause + ' == ' + totalLegs
whenClauses += 'n )n'
}
}
…
…Tortoises & Cranes: DSL…
…
def drl = '''
dialect "mvel"
rule "deduce animal counts"
when
''' + whenClauses + ''' then
''' + thenClauses + '''end
'''
def kbuilderConf = newKnowledgeBuilderConfiguration(null, loader)
def kbuilder = newKnowledgeBuilder(kbuilderConf)
kbuilder.add(newReaderResource(new StringReader(drl)), ResourceType.DRL)
def kbaseConf = newKnowledgeBaseConfiguration(null, loader)
def kbase = newKnowledgeBase(kbaseConf)
kbase.addKnowledgePackages(kbuilder.knowledgePackages)
def ksession = kbase.newStatefulKnowledgeSession()
(totalAnimals + 1).times { n ->
animals.each { key, val ->
def capKey = key.capitalize()
Class animal = loader.loadClass(capKey)
if (totalLegs.intdiv(animal.numLegs) >= n) {
ksession.insert(animal.newInstance(n))
}
}
}
ksession.fireAllRules()
ksession.dispose()
}
}
…
…Tortoises & Cranes: DSL…
…
@Field animalProps = [:]
def props = [:]
def methodMissing(String name, _have) {
new AnimalHolder(animals: animalProps, name: name)
}
def propertyMissing(String name) {
name
}
class ThereHolder {
def props
def methodMissing(String name, args) {
props['total' + args[0].capitalize()] = name.toInteger()
}
}
class AnimalHolder {
def animals, name
def methodMissing(String number, args) {
animals[name] = number.toInteger()
}
}
def there = { _are ->
new ThereHolder(props: props)
}
…Tortoises & Cranes: DSL
…
cranes have 2 legs
tortoises have 4 legs
//millipedes have 1000 legs
there are 7 animals
//there are 8 animals
there are 20 legs
//there are 1020 legs
new GroovyShell([animals: animalProps] as Binding).evaluate(
animalProps.collect { key, val ->
def capKey = key.capitalize()
"""
@groovy.transform.Immutable
class $capKey {
static int numLegs = $val
int quantity
}
"""
}.join('n') + "Solver.main(animals, $props.totalAnimals, $props.totalLegs, getClass().classLoader)"
)
Cranes 4
Tortoises 3
Cranes 4
Tortoises 3
Millipedes 1
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
Einstein’s Riddle
• Further Discussion
• More Info
©ASERT2006-2013
Einstein’s Riddle…
• Wikipedia: The zebra puzzle is a well-
known logic puzzle
– It is often called Einstein's Puzzle or Einstein's Riddle
because it is said to have been invented by Albert
Einstein as a boy, with the claim that Einstein said
“… only 2 percent of the world's population can solve it.”
– The puzzle is also sometimes attributed to Lewis Carroll.
However, there is no known evidence for Einstein's or
Carroll's authorship; and the original puzzle cited
mentions brands of cigarette, such as Kools, that did not
exist during Carroll's lifetime or Einstein's boyhood
©ASERT2006-2013
…Einstein’s Riddle
©ASERT2006-2013
• Some premises:
– The British person lives in the red house
– The Swede keeps dogs as pets
– The Dane drinks tea
– The green house is on the left of the white house
– The green homeowner drinks coffee
– The man who smokes Pall Mall keeps birds
– The owner of the yellow house smokes Dunhill
– The man living in the center house drinks milk
– The Norwegian lives in the first house
– The man who smokes Blend lives next to the one who keeps cats
– The man who keeps the horse lives next to the man who smokes Dunhill
– The man who smokes Bluemaster drinks beer
– The German smokes Prince
– The Norwegian lives next to the blue house
– The man who smokes Blend has a neighbor who drinks water
• And a question:
– Who owns the fish?
Einstein’s Riddle : Prolog
©ASERT2006-2013
% from http://www.baptiste-wicht.com/2010/09/solve-einsteins-riddle-using-
prolog
% Preliminary definitions
persons(0, []) :- !.
persons(N, [(_Men,_Color,_Drink,_Smoke,_Animal)|T]) :- N1 is N-1,
persons(N1,T).
person(1, [H|_], H) :- !.
person(N, [_|T], R) :- N1 is N-1, person(N1, T, R).
% The Brit lives in a red house
hint1([(brit,red,_, _, _)|_]).
hint1([_|T]) :- hint1(T).
% The Swede keeps dogs as pets
hint2([(swede,_,_,_,dog)|_]).
hint2([_|T]) :- hint2(T).
% The Dane drinks tea
hint3([(dane,_,tea,_,_)|_]).
hint3([_|T]) :- hint3(T).
% The Green house is on the left of the White house
hint4([(_,green,_,_,_),(_,white,_,_,_)|_]).
hint4([_|T]) :- hint4(T).
% The owner of the Green house drinks coffee.
Einstein’s Riddle : Polyglot
©ASERT2006-2013
@GrabResolver('http://dev.inf.unideb.hu:8090/archiva/repository/internal')
//@Grab('jlog:jlogic-debug:1.3.6')
@Grab('org.prolog4j:prolog4j-api:0.2.0')
// uncomment one of the next three lines
//@Grab('org.prolog4j:prolog4j-jlog:0.2.0')
@Grab('org.prolog4j:prolog4j-tuprolog:0.2.0')
//@Grab('org.prolog4j:prolog4j-jtrolog:0.2.0')
import org.prolog4j.*
def p = ProverFactory.prover
p.addTheory(new File('/GroovyExamples/tuProlog/src/einstein.pl').text)
def sol = p.solve("solution(Persons).")
//println sol.solution.get('Persons') // jlog to avoid converter
println sol.get('Persons') // jtrolog/tuProlog
Einstein’s Riddle : Polyglot w/ DSL…
©ASERT2006-2013
// define some domain classes and objects
enum Pet { dog, cat, bird, fish, horse }
enum Color { green, white, red, blue, yellow }
enum Smoke { dunhill, blends, pallmall, prince, bluemaster }
enum Drink { water, tea, milk, coffee, beer }
enum Nationality { Norwegian, Dane, Brit, German, Swede }
dogs = dog; birds = bird; cats = cat; horses = horse
a = owner = house = the = abode = person = man = is = to =
side = next = who = different = 'ignored'
// some preliminary definitions
p = ProverFactory.prover
hintNum = 1
p.addTheory('''
persons(0, []) :- !.
persons(N, [(_Men,_Color,_Drink,_Smoke,_Animal)|T]) :- N1 is N-1,
persons(N1,T).
person(1, [H|_], H) :- !.
person(N, [_|T], R) :- N1 is N-1, person(N1, T, R).
''')
…Einstein’s Riddle : Polyglot w/ DSL…
©ASERT2006-2013
// define some helper methods (our interface to prolog)
def addPairHint(Map m) {
def from = m.from?.toString()?.toLowerCase()
p.addTheory("""
hint$hintNum([(${from ?: '_'},${m.color ?: '_'},${m.drink ?: '_'},${m.smoke
?: '_'},${m.pet ?: '_'})|_]).
hint$hintNum([_|T]) :- hint$hintNum(T).
""")
hintNum++
}
def addPositionHint(Map m, int pos) {
def from = m.from?.toString()?.toLowerCase()
p.addTheory("""
hint$hintNum(Persons) :- person($pos, Persons, (${from ?: '_'},${m.color ?:
'_'},${m.drink ?: '_'},${m.smoke ?: '_'},${m.pet ?: '_'})).
""")
hintNum++
}
def addToLeftHint(Map left, Map right) {
p.addTheory("""
hint$hintNum([(_,$left.color,_,_,_),(_,$right.color,_,_,_)|_]).
hint$hintNum([_|T]) :- hint$hintNum(T).
""")
hintNum++
}
...
…Einstein’s Riddle : Polyglot w/ DSL…
©ASERT2006-2013
// now implement DSL in terms of helper methods
def the(Nationality n) {
def ctx = [from:n]
[
drinks: { d -> addPairHint(ctx + [drink:d]) },
smokes: { s -> addPairHint(ctx + [smoke:s]) },
keeps: { p -> addPairHint(ctx + [pet:p]) },
rears: { p -> addPairHint(ctx + [pet:p]) },
owns:{ _the -> [first:{ house -> addPositionHint(ctx, 1) }] },
has:{ _a ->
[pet: { a -> addPairHint(ctx + [pet:a]) }] +
Color.values().collectEntries{ c ->
[c.toString(), { _dummy -> addPairHint(ctx + [color:c]) } ]
}
},
lives: { _next -> [to: { _the ->
Color.values().collectEntries{ c ->
[c.toString(), { _dummy -> addNeighbourHint(ctx, [color:c]) } ]
}
}]}
]
}
...
…Einstein’s Riddle : Polyglot w/ DSL…
©ASERT2006-2013
// now define the DSL
the man from the centre house drinks milk
the Norwegian owns the first house
the Dane drinks tea
the German smokes prince
the Swede keeps dogs // alternate ending: has a pet dog
the Brit has a red house // alternate ending: red abode
the owner of the green house drinks coffee
the owner of the yellow house smokes dunhill
the person known to smoke pallmall rears birds // other ending: keeps birds
the man known to smoke bluemaster drinks beer
the green house is on the left side of the white house
the man known to smoke blends lives next to the one who keeps cats
the man known to keep horses lives next to the man who smokes dunhill
the man known to smoke blends lives next to the one who drinks water
the Norwegian lives next to the blue house
…Einstein’s Riddle : Polyglot w/ DSL…
©ASERT2006-2013
// now implement DSL in terms of helper methods
def the(Nationality n) {
def ctx = [from:n]
[
drinks: { d -> addPairHint(ctx + [drink:d]) },
smokes: { s -> addPairHint(ctx + [smoke:s]) },
keeps: { p -> addPairHint(ctx + [pet:p]) },
...
]
}
...
the German smokes prince
the(German).smokes(prince)
n = German
ctx = [from: German]
[drinks: …,
smokes: { s -> addPairHint([from: German, smoke: s]) },
keeps: …,
…
] addPairHint([from: German, smoke: prince])
…Einstein’s Riddle : Polyglot w/ DSL…
• Some parts of our DSL are automatically
statically inferred, e.g. typing ‘bl’ and then
asking for completion yields:
• But other parts are not known, e.g. the
word ‘house’ in the fragment below:
©ASERT2006-2013
‘house’ is key for a Map and could be any value
…Einstein’s Riddle : Polyglot w/ DSL
©ASERT2006-2013
class HousePlaceHolder {
def c1, script
def house(_is) {
[on: { _the -> [left: { _side -> [of: { __the ->
Color.values().collectEntries { c2 ->
[c2.toString(), { _dummy -> script.addToLeftHint(
[color: c1], [color: c2] )}]}
}]}]}]
}
}
def the(Color c1) { new HousePlaceHolder(c1:c1, script:this) }
def the(Color c1) {[
house: { _is -> [on: { _the -> [left: { _side -> [of: { __the ->
Color.values().collectEntries{ c2 -> [c2.toString(), { _dummy ->
addToLeftHint([color:c1], [color:c2])
}]}
}]}]}]}
]}
‘house’ is now understood
We can choose to introduce
additional static typing
information into our DSL
implementation or ‘teach’
our IDE about or DSL.
Einstein’s Riddle : Choco DSL…
©ASERT2006-2013
//@GrabResolver('http://www.emn.fr/z-info/choco-repo/mvn/repository')
//@Grab('choco:choco-solver:2.1.5')
import static choco.Choco.*
import choco.kernel.model.variables.integer.*
import groovy.transform.Field
enum Pet { dog, cat, bird, fish, horse }
enum Color { green, white, red, blue, yellow }
enum Sport { baseball, volleyball, football, hockey, tennis }
enum Drink { water, tea, milk, coffee, beer }
enum Nationality { Norwegian, Dane, Briton, German, Swede }
import static Pet.*
import static Color.*
import static Sport.*
import static Drink.*
import static Nationality.*
// define logic solver data structures
num = 5
center = 2
first = 0
println "Solving Einstein's Riddle:"
…
…Einstein’s Riddle : Choco DSL…
©ASERT2006-2013
…
@Field m = new choco.cp.model.CPModel()
def s = new choco.cp.solver.CPSolver()
choco.Choco.metaClass.static.eq = { c, v -> delegate.eq(c, v.ordinal()) }
def makeEnumVar(st, arr) { choco.Choco.makeIntVar(st, 0, arr.size()-1,
choco.Options.V_ENUM) }
pets = new IntegerVariable[num]
colors = new IntegerVariable[num]
plays = new IntegerVariable[num]
drinks = new IntegerVariable[num]
nations = new IntegerVariable[num]
(0..<num).each { i ->
pets[i] = makeEnumVar("pet$i", pets)
colors[i] = makeEnumVar("color$i", colors)
plays[i] = makeEnumVar("plays$i", plays)
drinks[i] = makeEnumVar("drink$i", drinks)
nations[i] = makeEnumVar("nation$i", nations)
}
def pretty(s, c, arr, i) { c.values().find{ it.ordinal() ==
s.getVar(arr[i])?.value } }
…
…Einstein’s Riddle : Choco DSL…
©ASERT2006-2013
…
// define DSL (simplistic non-refactored version)
def neighbours(var1, val1, var2, val2) {
m.addConstraint and(
ifOnlyIf(eq(var1[0], val1), eq(var2[1], val2)),
implies(eq(var1[1], val1), or(eq(var2[0], val2), eq(var2[2], val2))),
implies(eq(var1[2], val1), or(eq(var2[1], val2), eq(var2[3], val2))),
implies(eq(var1[3], val1), or(eq(var2[2], val2), eq(var2[4], val2))),
ifOnlyIf(eq(var1[4], val1), eq(var2[3], val2))
)
}
iff = { e1, c1, e2, c2 -> m.addConstraint and(*(0..<num).collect{
ifOnlyIf(eq(e1[it], c1), eq(e2[it], c2)) }) }
isEq = { a, b -> m.addConstraint eq(a, b) }
dogs = dog; birds = bird; cats = cat; horses = horse
a = owner = house = the = abode = person = man = to = is = side = next = who =
different = 'ignored'
…
…Einstein’s Riddle : Choco DSL…
©ASERT2006-2013
…
// define the DSL in terms of DSL implementation
def the(Nationality n) {
def ctx = [nations, n]
[
drinks:iff.curry(*ctx, drinks),
plays:iff.curry(*ctx, plays),
keeps:iff.curry(*ctx, pets),
rears:iff.curry(*ctx, pets),
owns:{ _the -> [first:{ house -> isEq(nations[first], n)}] },
has:{ _a ->
[pet:iff.curry(*ctx, pets)] + Color.values().collectEntries{ c ->
[c.toString(), { _dummy -> iff(*ctx, colors, c) } ]
}
},
lives: { _next -> [to: { _the ->
Color.values().collectEntries{ c ->
[c.toString(), { _dummy -> neighbours(*ctx, colors, c) } ]
}
}]}
]
}
…
…Einstein’s Riddle : Choco DSL…
©ASERT2006-2013
…
def the(Color c1) {[
house: { _is -> [on: { _the -> [left: { _side -> [of: { __the ->
Color.values().collectEntries{ c2 -> [c2.toString(), { _dummy ->
m.addConstraint and(*(1..<num).collect{ ifOnlyIf(eq(colors[it-1], c1),
eq(colors[it], c2)) })
}]}
}]}]}]}
]}
def the(String _dummy) {[
of:{ _the ->
Color.values().collectEntries{ c -> [c.toString(), { _house -> [
drinks:iff.curry(colors, c, drinks), plays:iff.curry(colors, c, plays)
] } ] }
},
known: { _to -> [
play: { sport ->
def ctx = [plays, sport]
[
rears: iff.curry(*ctx, pets),
keeps: iff.curry(*ctx, pets),
drinks: iff.curry(*ctx, drinks),
lives: { _next -> [to: { _the -> [one: { _who -> [
keeps: { pet -> neighbours(pets, pet, *ctx) },
drinks: { beverage -> neighbours(drinks, beverage, *ctx) }
]}]}]}
]
},
…
…Einstein’s Riddle : Choco DSL…
©ASERT2006-2013
…
keep : { pet -> [
lives: { _next -> [to: { _the -> [man: { _who -> [
plays: { sport -> neighbours(pets, pet, plays, sport) }
]}]}]}
]}
]},
from: { _the -> [center: { house ->
[drinks: { d -> isEq(drinks[center], d)}]
}]}
]}
def all(IntegerVariable[] var) {
[are: { _different -> m.addConstraint allDifferent(var) } ]
}
…
…Einstein’s Riddle : Choco DSL
©ASERT2006-2013
…
// define rules
all pets are different
all colors are different
all plays are different
all drinks are different
all nations are different
the man from the center house drinks milk
the Norwegian owns the first house
the Dane drinks tea
the German plays hockey
the Swede keeps dogs // alternate ending: has a pet dog
the Briton has a red house // alternate ending: red abode
the owner of the green house drinks coffee
the owner of the yellow house plays baseball
the person known to play football rears birds // alternate ending: keeps birds
the man known to play tennis drinks beer
the green house is on the left side of the white house
the man known to play volleyball lives next to the one who keeps cats
the man known to keep horses lives next to the man who plays baseball
the man known to play volleyball lives next to the one who drinks water
the Norwegian lives next to the blue house
…
…Einstein’s Riddle : Choco…
©ASERT2006-2013
…
// invoke logic solver
s.read(m)
def more = s.solve()
while (more) {
for (i in 0..<num) {
print 'The ' + pretty(s, Nationality, nations, i)
print ' has a pet ' + pretty(s, Pet, pets, i)
print ' plays ' + pretty(s, Sport, plays, i)
print ' drinks ' + pretty(s, Drink, drinks, i)
println ' and lives in a ' + pretty(s, Color, colors, i) + ' house'
}
more = s.nextSolution()
}
Einstein’s Riddle : Output
©ASERT2006-2013
Solving Einstein's Riddle:
The Norwegian has a pet cat plays baseball drinks water and lives in a yellow house
The Dane has a pet horse plays volleyball drinks tea and lives in a blue house
The Briton has a pet bird plays football drinks milk and lives in a red house
The German has a pet fish plays hockey drinks coffee and lives in a green house
The Swede has a pet dog plays tennis drinks beer and lives in a white house
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
Further Discussion
• More Info
©ASERT2006-2013
Discussion points
• Choosing granularity
• Choosing the level of dynamic/static typing
• Multi-paradigm solutions
• Capturing Rule Design Patterns using AST
transforms
Granularity
Solve manners2009
Neighbours must share a hobby
Neighbours are of a different gender
There should be 2 doctors at each table
Each doctor at a table should be a different kind
...
The Guest at position 2 on table 1 should have a
different gender to the Guest at position 1
The Guest at position 2 on table 1 should have a
different gender to the Guest at position 3
...
Typing…
• Dynamic
• Traditional Static Typing
• Stronger levels of Static Typing
…Typing…
import groovy.transform.TypeChecked
import experimental.SprintfTypeCheckingVisitor
@TypeChecked(visitor=SprintfTypeCheckingVisitor)
void main() {
sprintf('%s will turn %d on %tF', 'John', new Date(), 21)
}
[Static type checking] - Parameter types didn't match types
expected from the format String:
For placeholder 2 [%d] expected 'int' but was 'java.util.Date'
For placeholder 3 [%tF] expected 'java.util.Date' but was 'int'
sprintf has an Object varargs
parameter, hence not normally
amenable to further static checking
but for constant Strings we can do
better using a custom type checking
plugin.
…Typing…
import groovy.transform.TypeChecked
import tictactoe.*
Import static tictactoe.Position.*
@TypeChecked(visitor=TicTacToeTypeVisitor)
void main() {
Board.empty().move(NW).move(C).move(W).move(SW).move(SE)
}
package tictactoe
enum Position {
NW, N, NE, W, C, E, SW, S, SE
}
class Board {
static Board empty() { new Board() }
Board move(Position p) { this }
}
…Typing
import groovy.transform.TypeChecked
import tictactoe.*
Import static tictactoe.Position.*
@TypeChecked(visitor=TicTacToeTypeVisitor)
void main() {
Board.empty().move(NW).move(C).move(W).move(SW).move(SE)
}
package tictactoe
enum Position {
NW, N, NE, W, C, E, SW, S, SE
}
[Static type checking] - Attempt to call suboptimal
move SE not allowed [HINT: try NE]
Custom type checker which fails
compilation if programmer attempts
to code a suboptimal solution. Where
suboptimal means doesn’t agree with
what is returned by a minimax,
alpha-beta pruning, iterative
deepening solving engine.
Multi-paradigm solutions
• Imperative
• Functional
– Leveraging immutable data structures
– Persistent data structures
– Higher-order functions
• Rules-based
• Concurrency, e.g. Gpars
– Data Parallelism: Map, Reduce
– DataFlow
– Others: Fork Join, Actors
Using compile-time Metaprogramming
• Powerful mechanism
– As illustrated by GOF examples
– @Immutable, @Delegate and others
• Rich area for further research
– Explore whether rules design patterns can be readily
embodied within AST transforms
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
More Info
©ASERT2006-2013
More Information: URLs
• Groovy
– http://groovy.codehaus.org/
• Groovy DSL talk in general
– http://www.slideshare.net/glaforge/groovy-domain-specific-
languages-springone2gx-2013
• Groovy & Other Paradigms
– http://www.slideshare.net/paulk_asert/concurrency-with-gpars
– http://www.slideshare.net/paulk_asert/functional-groovy
• Drools Expert & Planner
– http://www.jboss.org/drools/
• Choco
– http://www.emn.fr/z-info/choco-solver/
More Information: Groovy in Action 2ed
Contains a
chapter on
DSLs!

Contenu connexe

Similaire à Leveraging Groovy for Capturing Business Rules

concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GParsPaul King
 
Groovy and Grails
Groovy and GrailsGroovy and Grails
Groovy and GrailsGiltTech
 
Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Guillaume Laforge
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005Tugdual Grall
 
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGuillaume Laforge
 
Javaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 GroovytestingJavaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 GroovytestingAndres Almiray
 
Boosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyBoosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyJames Williams
 
GTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyGTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyAndres Almiray
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadJava9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadDavid Gómez García
 
Groovy for Java Developers
Groovy for Java DevelopersGroovy for Java Developers
Groovy for Java DevelopersAndres Almiray
 
Building a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to SpaceBuilding a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to SpaceMaarten Balliauw
 
Oscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneOscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneAndres Almiray
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle GroovyDeepak Bhagat
 
Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Jesper Kamstrup Linnet
 
Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Jim Shingler
 
Turtle Graphics in Groovy
Turtle Graphics in GroovyTurtle Graphics in Groovy
Turtle Graphics in GroovyJim Driscoll
 
Introduction to Modern Perl
Introduction to Modern PerlIntroduction to Modern Perl
Introduction to Modern PerlDave Cross
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartGabriele Lana
 

Similaire à Leveraging Groovy for Capturing Business Rules (20)

concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GPars
 
Groovy and Grails
Groovy and GrailsGroovy and Grails
Groovy and Grails
 
Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005
 
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
 
Javaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 GroovytestingJavaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 Groovytesting
 
Boosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyBoosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with Groovy
 
GTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyGTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with Groovy
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadJava9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidad
 
Groovy for Java Developers
Groovy for Java DevelopersGroovy for Java Developers
Groovy for Java Developers
 
Building a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to SpaceBuilding a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to Space
 
HDF5 Tools
HDF5 ToolsHDF5 Tools
HDF5 Tools
 
Oscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneOscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast Lane
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle Groovy
 
Prototype Js
Prototype JsPrototype Js
Prototype Js
 
Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?
 
Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Gg Code Mash2009 20090106
Gg Code Mash2009 20090106
 
Turtle Graphics in Groovy
Turtle Graphics in GroovyTurtle Graphics in Groovy
Turtle Graphics in Groovy
 
Introduction to Modern Perl
Introduction to Modern PerlIntroduction to Modern Perl
Introduction to Modern Perl
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing Part
 

Plus de Paul King

groovy databases
groovy databasesgroovy databases
groovy databasesPaul King
 
groovy transforms
groovy transformsgroovy transforms
groovy transformsPaul King
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gparsPaul King
 
tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovyPaul King
 
functional groovy
functional groovyfunctional groovy
functional groovyPaul King
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing GroovyPaul King
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing PracticesPaul King
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expertPaul King
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language PracticesPaul King
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More GroovyPaul King
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power FeaturesPaul King
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing GroovyPaul King
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009Paul King
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...Paul King
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy TutorialPaul King
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Paul King
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with GroovyPaul King
 

Plus de Paul King (20)

groovy databases
groovy databasesgroovy databases
groovy databases
 
groovy transforms
groovy transformsgroovy transforms
groovy transforms
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gpars
 
tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovy
 
functional groovy
functional groovyfunctional groovy
functional groovy
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing Groovy
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing Practices
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expert
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
GroovyDSLs
GroovyDSLsGroovyDSLs
GroovyDSLs
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language Practices
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More Groovy
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power Features
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing Groovy
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy Tutorial
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with Groovy
 

Dernier

Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESmohitsingh558521
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 

Dernier (20)

Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 

Leveraging Groovy for Capturing Business Rules

  • 2. Topics Introduction to DSLs • Introduction to Groovy • DSLs in Groovy • Why Groovy? • Tortoise & Crane Example • Einstein’s Riddle • Further Discussion • More Info ©ASERT2006-2013
  • 3. What is a DSL? • A domain-specific language is a programming language or executable specification language that offers, through appropriate notations and abstractions, expressive power focused on, and usually restricted to, a particular problem domain – declarative data << DSL << general-purpose programming language (GPL) – AKA: fluent / human interfaces, language oriented programming, problem-oriented languages, little / mini languages, macros, business natural languages Sources: http://en.wikipedia.org/wiki/Domain-specific_language van Deursen, A., Klint, P., Visser, J.: Domain-specific languages: an annotated bibliography. ACM SIGPLAN Notices 35 (2000) 26–36 ©ASERT2006-2013
  • 4. Goals of DSLs • Use a more expressive language than a general-purpose one • Share a common metaphor of understanding between developers and subject matter experts • Have domain experts help with the design of the business logic of an application • Avoid cluttering business code with boilerplate technical code thanks to a clean separation • Let business rules have their own lifecycle
  • 5. Why Scripting DSLs package org.drools.examples.golfing; dialect "mvel" import org.drools.examples.golfing.GolfingExample.Golfer; rule "find solution" when // Bob is wearing plaid pants $bob : Golfer( name == "Bob", color == "plaid") // ... then System.out.println( "Bob " + $bob.getColor() ); end package org.drools.examples.golfing; dialect "mvel" import org.drools.examples.golfing.GolfingExample.Golfer; rule "find solution" when Bob is wearing plaid pants // ... then Display all details end when Bob is wearing plaid pants display all details Compile time translation Compile time or runtime translation
  • 6. Why a DSL? • Advantages: – Domain experts can understand, validate, modify, and often even develop DSL programs – Self-documenting (?) – Enhance quality, productivity, reliability, maintainability, portability and reusability – Safety; language constructs can be made safe – Tooling • Disadvantages: – Learning cost vs. limited applicability – Cost of designing, implementing & maintaining DSL & tools – Attaining proper scope – Trade-offs between DSL specific and general- purpose programming language constructs – Efficiency costs – Proliferation of similar non-standard DSLs – Tooling ©ASERT2006-2013
  • 7. Topics • Introduction to DSLs Introduction to Groovy • DSLs in Groovy • Why Groovy? • Tortoise & Crane Example • Einstein’s Riddle • Further Discussion • More Info ©ASERT2006-2013
  • 8. Java code for list manipulation ©ASERT2006-2013 import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main main = new Main(); List shortNames = main.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } Based on an example by Jim Weirich & Ted Leung
  • 9. Groovy code for list manipulation ©ASERT2006-2013 import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main main = new Main(); List shortNames = main.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } Rename Main.java to Main.groovy
  • 10. Some Java Boilerplate identified ©ASERT2006-2013 import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main main = new Main(); List shortNames = main.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } Are the semicolons needed? And shouldn’t we us more modern list notation? Why not import common libraries? Do we need the static types? Must we always have a main method and class definition? How about improved consistency?
  • 11. Java Boilerplate removed ©ASERT2006-2013 def keepShorterThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() < length) { result.add(s) } } return result } names = new ArrayList() names.add("Ted"); names.add("Fred") names.add("Jed"); names.add("Ned") System.out.println(names) shortNames = keepShorterThan(names, 4) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) }
  • 12. More Java Boilerplate identified ©ASERT2006-2013 def keepShorterThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() < length) { result.add(s) } } return result } names = new ArrayList() names.add("Ted"); names.add("Fred") names.add("Jed"); names.add("Ned") System.out.println(names) shortNames = keepShorterThan(names, 4) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) } Shouldn’t we have special notation for lists? And special facilities for list processing? Is ‘return’ needed at end? Is the method now needed? Simplify common methods? Remove unambiguous brackets?
  • 13. Boilerplate removed = nicer Groovy version ©ASERT2006-2013 names = ["Ted", "Fred", "Jed", "Ned"] println names shortNames = names.findAll{ it.size() < 4 } println shortNames.size() shortNames.each{ println it } ["Ted", "Fred", "Jed", "Ned"] 3 Ted Jed Ned Output:
  • 14. Or Groovy DSL version if required ©ASERT2006-2013 names = [] def of, having, less = null def given(_the) { [names:{ Object[] ns -> names.addAll(ns) [and: { n -> names += n }] }] } def the = [ number: { _of -> [names: { _having -> [size: { _less -> [than: { size -> println names.findAll{ it.size() < size }.size() }]}] }] }, names: { _having -> [size: { _less -> [than: { size -> names.findAll{ it.size() < size }.each{ println it } }]}] } ] def all = [ the: { println names } ] def display(arg) { arg } given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4
  • 15. Closures ©ASERT2006-2013 int twice(int arg) { arg * 2 } def triple = { int arg -> arg * 3 } println twice(3) // => 6 println triple(3) // => 9
  • 16. Grapes / Grab: Google collections ©ASERT2006-2013 @Grab('com.google.guava:guava:r09') import com.google.common.collect.HashBiMap HashBiMap fruit = [grape:'purple', lemon:'yellow', lime:'green'] assert fruit.lemon == 'yellow' assert fruit.inverse().yellow == 'lemon'
  • 17. ©ASERT2006-2011 Groovy Builders <html> <head> <title>Hello</title> </head> <body> <ul> <li>world 1</li> <li>world 2</li> <li>world 3</li> <li>world 4</li> <li>world 5</li> </ul> </body> </html> import groovy.xml.* def page = new MarkupBuilder() page.html { head { title 'Hello' } body { ul { for (count in 1..5) { li "world $count" } } } } • Markup Builder
  • 18. Topics • Introduction to DSLs • Introduction to Groovy DSLs in Groovy • Why Groovy? • Tortoise & Crane Example • Einstein’s Riddle • Further Discussion • More Info ©ASERT2006-2013
  • 19. DSL example... ©ASERT2006-2013 class FluentApi { def action, what def the(what) { this.what = what; this } def of(arg) { action(what(arg)) } } show = { arg -> println arg } square_root = { Math.sqrt(it) } please = { new FluentApi(action: it) } please show the square_root of 100 // => 10.0
  • 20. …DSL example... ©ASERT2006-2013 class FluentApi { def action, what def the(what) { this.what = what; this } def of(arg) { action(what(arg)) } } show = { arg -> println arg } square_root = { Math.sqrt(it) } please = { new FluentApi(action: it) } please show the square_root of 100 // => 10.0 DSL implementation details
  • 21. …DSL example... ©ASERT2006-2013 class FluentApi { def action, what def the(what) { this.what = what; this } def of(arg) { action(what(arg)) } } show = { arg -> println arg } square_root = { Math.sqrt(it) } please = { new FluentApi(action: it) } please show the square_root of 100 // => 10.0 DSL usage
  • 23. …DSL example... ©ASERT2006-2013 please show the square_root of 100 please(show).the(square_root).of(100)
  • 24. …DSL example... ©ASERT2006-2013 class FluentApi { def action, what def the(what) { this.what = what; this } def of(arg) { action(what(arg)) } } show = { arg -> println arg } square_root = { Math.sqrt(it) } please = { new FluentApi(action: it) } please(show).the(square_root).of(100)
  • 25. …DSL example... ©ASERT2006-2013 Object.metaClass.please = { clos -> clos(delegate) } Object.metaClass.the = { clos -> delegate[1](clos(delegate[0])) } show = { thing -> [thing, { println it }] } square_root = { Math.sqrt(it) } given = { it } given 100 please show the square_root // ==> 10.0
  • 26. ...DSL example... ©ASERT2006-2013 show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please show the square_root of 100 // ==> 10.0
  • 27. ...DSL example... ©ASERT2006-2013 show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please show the square_root of 100 // ==> 10.0 Inspiration for this example came from …
  • 28. ...DSL example ©ASERT2006-2013 // Japanese DSL using GEP3 rules Object.metaClass.を = Object.metaClass.の = { clos -> clos(delegate) } まず = { it } 表示する = { println it } 平方根 = { Math.sqrt(it) } まず 100 の 平方根 を 表示する // First, show the square root of 100 // => 10.0 source: http://d.hatena.ne.jp/uehaj/20100919/1284906117 also: http://groovyconsole.appspot.com/edit/241001
  • 29. Topics • Introduction to DSLs • Introduction to Groovy • DSLs in Groovy Why Groovy? • Tortoise & Crane Example • Einstein’s Riddle • Further Discussion • More Info ©ASERT2006-2013
  • 30. Groovy provides • A flexible and malleable syntax – scripts, native syntax constructs (list, map, ranges) • Closures, less punctuation... – Compile-time and runtime meta-programming – metaclasses, AST transformations – also operator overloading • The ability to easily integrate into Java, app’n server apps – compile into bytecode or leave in source form – also security and safety
  • 32. @Immutable... • Java Immutable Class – As per Joshua Bloch Effective Java ©ASERT2006-2013 public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } }
  • 33. ...@Immutable... • Java Immutable Class – As per Joshua Bloch Effective Java ©ASERT2006-2013 public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } } boilerplate
  • 35. Rules engines • Backward chaining starts with a list of goals (or a hypothesis) and works backwards applying rules to derive new hypotheses until available data is found to support the hypotheses or all rules and data have been exhausted – http://en.wikipedia.org/wiki/Backward_chaining • Forward chaining starts with the available facts and applies rules to derive or infer more facts until a goal is reached – http://en.wikipedia.org/wiki/Forward_chaining
  • 36. Backward chaining example • Rules – If X croaks and eats flies – Then X is a frog – If X chirps and sings – Then X is a canary – If X is a frog – Then X is green – If X is a canary – Then X is yellow • Facts – Fritz croaks – Fritz eats flies – Tweety eats flies – Tweety chirps – Tweety is yellow Who is a frog? ? is a frog Based on rule 1, the computer can derive: 2. ? croaks and eats flies Based on logic, the computer can derive: 3. ? croaks and ? eats flies Based on the facts, the computer can derive: 4. Fritz croaks and Fritz eats flies
  • 37. Forward chaining example • Rules – If X croaks and eats flies – Then X is a frog – If X chirps and sings – Then X is a canary – If X is a frog – Then X is green – If X is a canary – Then X is yellow • Facts – Fritz croaks – Fritz eats flies – Tweety eats flies – Tweety chirps – Tweety is yellow Who is a frog? 1. Fritz croaks and Fritz eats flies Based on logic, the computer can derive: 2. Fritz croaks and eats flies Based on rule 1, the computer can derive: 3. Fritz is a frog
  • 38. Drools Expert Golfing Example…/* * Copyright 2010 JBoss Inc * Licensed under the Apache License… */ package org.drools.examples.golfing; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.ResourceFactory; import org.drools.runtime.StatefulKnowledgeSession; public class GolfingExample { /** * @param args */ public static void main(final String[] args) { final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("golf.drl", GolfingExample.class), ResourceType.DRL); final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages(kbuilder.getKnowledgePackages()); final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); String[] names = new String[]{"Fred", "Joe", "Bob", "Tom"}; String[] colors = new String[]{"red", "blue", "plaid", "orange"}; int[] positions = new int[]{1, 2, 3, 4}; for (int n = 0; n < names.length; n++) { for (int c = 0; c < colors.length; c++) { for (int p = 0; p < positions.length; p++) { ksession.insert(new Golfer(names[n], colors[c], positions[p])); } } } // ... // ... ksession.fireAllRules(); ksession.dispose(); } public static class Golfer { private String name; private String color; private int position; public Golfer() { } public Golfer(String name, String color, int position) { super(); this.name = name; this.color = color; this.position = position; } /** * @return the color */ public String getColor() { return this.color; } /** * @return the name */ public String getName() { return this.name; } /** * @return the name */ public int getPosition() { return this.position; } } }
  • 39. …Drools Expert Golfing Example import groovy.transform.Immutable import static org.drools.builder.ResourceType.DRL import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newClassPathResource def kbuilder = newKnowledgeBuilder() kbuilder.add(newClassPathResource("golf.drl", getClass()), DRL) def kbase = newKnowledgeBase() kbase.addKnowledgePackages(kbuilder.knowledgePackages) def ksession = kbase.newStatefulKnowledgeSession() def names = ["Fred", "Joe", "Bob", "Tom"] def colors = ["red", "blue", "plaid", "orange"] def positions = [1, 2, 3, 4] [names, colors, positions].combinations().each { n, c, p -> ksession.insert(new Golfer(n, c, p)) } ksession.fireAllRules() ksession.dispose() @Immutable class Golfer { String name String color int position }
  • 40. …Drools Expert Golfing Example import groovy.transform.Immutable import static org.drools.builder.ResourceType.DRL import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newClassPathResource def kbuilder = newKnowledgeBuilder() kbuilder.add(newClassPathResource("golf.drl", getClass()), DRL) def kbase = newKnowledgeBase() kbase.addKnowledgePackages(kbuilder.knowledgePackages) def ksession = kbase.newStatefulKnowledgeSession() def names = ["Fred", "Joe", "Bob", "Tom"] def colors = ["red", "blue", "plaid", "orange"] def positions = [1, 2, 3, 4] [names, colors, positions].combinations().each { n, c, p -> ksession.insert(new Golfer(n, c, p)) } ksession.fireAllRules() ksession.dispose() @Immutable class Golfer { String name String color int position }
  • 41. …Drools Expert Golfing Example import groovy.transform.Immutable import static org.drools.builder.ResourceType.DRL import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newClassPathResource def kbuilder = newKnowledgeBuilder() kbuilder.add(newClassPathResource("golf.drl", getClass()), DRL) def kbase = newKnowledgeBase() kbase.addKnowledgePackages(kbuilder.knowledgePackages) def ksession = kbase.newStatefulKnowledgeSession() def names = ["Fred", "Joe", "Bob", "Tom"] def colors = ["red", "blue", "plaid", "orange"] def positions = [1, 2, 3, 4] [names, colors, positions].combinations().each { n, c, p -> ksession.insert(new Golfer(n, c, p)) } ksession.fireAllRules() ksession.dispose() @Immutable class Golfer { String name String color int position }
  • 42. @Grab… • Set up dependencies as per Java – E.g. manually add to classpath or use Maven/Gradle/Ant or rely on IDE features • Or @Grab declares dependencies inline – Makes scripts environment independent – Downloads transient dependencies as needed – (Uncomment in github examples) //@GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/') //@Grab('org.drools:drools-compiler:5.5.0.Final') //@Grab('org.drools:drools-core:5.5.0.Final') //@Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1;transitive=false') //@Grab('com.google.protobuf:protobuf-java:2.4.1') //@Grab('org.slf4j:slf4j-simple:1.6.4') import groovy.transform.Immutable import org.drools.builder.ResourceType import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newClassPathResource def kbuilder = newKnowledgeBuilder() // ...
  • 43. …@Grab… > groovy -classpath C:ProjectsGroovyProblemSolversoutproductionDroolsExpert; C:Userspaulk.groovygrapesorg.droolsdrools-compilerjarsdrools-compiler-5.3.3.Final.jar; C:Userspaulk.groovygrapesorg.antlrantlr-runtimejarsantlr-runtime-3.3.jar; C:Userspaulk.groovygrapesorg.antlrantlrjarsantlr-3.3.jar; C:Userspaulk.groovygrapesorg.antlrstringtemplatejarsstringtemplate-3.2.1.jar; C:Userspaulk.groovygrapesantlrantlrjarsantlr-2.7.7.jar; C:Userspaulk.groovygrapesorg.eclipse.jdt.core.compilerecjjarsecj-3.5.1.jar; C:Userspaulk.groovygrapesorg.mvelmvel2jarsmvel2-2.1.0.drools16.jar; C:Userspaulk.groovygrapescom.sun.xml.bindjaxb-xjcjarsjaxb-xjc-2.2.5.jboss-1.jar; C:Userspaulk.groovygrapescom.sun.xml.bindjaxb-impljarsjaxb-impl-2.2.5.jboss-1.jar; C:Userspaulk.groovygrapesjavax.xml.bindjaxb-apijarsjaxb-api-2.2.6.jar; C:Userspaulk.groovygrapescom.sun.istackistack-commons-runtimejarsistack-commons-runtime-2.6.1.jar; C:Userspaulk.groovygrapesjavax.xml.streamstax-apijarsstax-api-1.0-2.jar; C:Userspaulk.groovygrapesjavax.activationactivationjarsactivation-1.1.jar; C:Userspaulk.groovygrapescom.sun.xml.txw2txw2jarstxw2-20110809.jar; C:Userspaulk.groovygrapesrelaxngDatatyperelaxngDatatypejarsrelaxngDatatype-20020414.jar; C:Userspaulk.groovygrapescom.sun.codemodelcodemodeljarscodemodel-2.6.jar; C:Userspaulk.groovygrapescom.sun.xml.dtd-parserdtd-parserjarsdtd-parser-1.1.jboss-1.jar; C:Userspaulk.groovygrapescom.sun.istackistack-commons-toolsjarsistack-commons-tools-2.6.1.jar; C:Userspaulk.groovygrapesorg.apache.antantjarsant-1.7.0.jar; C:Userspaulk.groovygrapesorg.apache.antant-launcherjarsant-launcher-1.7.0.jar; C:Userspaulk.groovygrapesorg.kohsuke.rngomrngomjarsrngom-201103.jboss-1.jar; C:Userspaulk.groovygrapescom.sun.xsomxsomjarsxsom-20110809.jar; C:Userspaulk.groovygrapesxml-resolverxml-resolverjarsxml-resolver-1.1.jar; C:Userspaulk.groovygrapesorg.droolsdrools-corejarsdrools-core-5.3.3.Final.jar; C:Userspaulk.groovygrapesorg.droolsknowledge-apijarsknowledge-api-5.3.3.Final.jar; C:ProjectsGroovyProblemSolversDroolsExpertsrcresources GolfExample.groovy Or you can precompile: > groovyc -classpath ... GolfExample.groovy > jar ... Then use groovy or java commands to run. Old school
  • 44. …@Grab… > groovy GolfExample.groovy @GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/') @Grab('org.drools:drools-compiler:5.5.0.Final') @Grab('org.drools:drools-core:5.5.0.Final') @Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1;transitive=false') @Grab('com.google.protobuf:protobuf-java:2.4.1') @Grab('org.slf4j:slf4j-simple:1.6.4') import groovy.transform.Immutable import org.drools.builder.ResourceType import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newClassPathResource def kbuilder = newKnowledgeBuilder() // ... With @Grab
  • 45. …@Grab > groovy GolfExample With @Grab @GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/') @Grab('org.drools:drools-compiler:5.5.0.Final') @Grab('org.drools:drools-core:5.5.0.Final') @Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1;transitive=false') @Grab('com.google.protobuf:protobuf-java:2.4.1') @Grab('org.slf4j:slf4j-simple:1.6.4') import groovy.transform.Immutable import org.drools.builder.ResourceType import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newClassPathResource def kbuilder = newKnowledgeBuilder() // ...
  • 46. golf.drl… dialect "mvel" import Golfer; rule "find solution" when // There is a golfer named Fred, $fred : Golfer( name == "Fred" ) // Joe is in position 2 $joe : Golfer( name == "Joe", position == 2, position != $fred.position, color != $fred.color ) // Bob is wearing plaid pants $bob : Golfer( name == "Bob", position != $fred.position, position != $joe.position, color == "plaid", color != $fred.color, color != $joe.color ) // ...
  • 47. …golf.drl // Tom isn't in position 1 or 4 // and isn't wearing orange $tom : Golfer( name == "Tom", position != 1, position != 4, position != $fred.position, position != $joe.position, position != $bob.position, color != "orange", color != $fred.color, color != $joe.color, color != $bob.color ) // The golfer to Fred's immediate right // is wearing blue pants Golfer( position == ( $fred.position + 1 ), color == "blue", this in ( $joe, $bob, $tom ) ) then System.out.println("Fred " + $fred.getPosition() + " " + $fred.getColor()); System.out.println("Joe " + $joe.getPosition() + " " + $joe.getColor()); System.out.println("Bob " + $bob.getPosition() + " " + $bob.getColor()); System.out.println("Tom " + $tom.getPosition() + " " + $tom.getColor()); end
  • 48. Topics • Introduction to DSLs • Introduction to Groovy • DSLs in Groovy • Why Groovy? Tortoise & Crane Example • Einstein’s Riddle • Further Discussion • More Info ©ASERT2006-2013
  • 49. Tortoises & Cranes • Around a pond dwell tortoises and cranes • There are 7 animals in total • There are 20 legs in total • How many of each animal are there? Source: http://www.youtube.com/watch?v=tUs4olWQYS4
  • 50. Tortoises & Cranes: Choco… //@GrabResolver('http://www.emn.fr/z-info/choco-repo/mvn/repository') //@Grab('choco:choco-solver:2.1.5') import static choco.Choco.* import choco.cp.model.CPModel import choco.cp.solver.CPSolver def m = new CPModel() def s = new CPSolver() def totalAnimals = 7 def totalLegs = 20 def c = makeIntVar('Cranes', 0, totalAnimals) def t = makeIntVar('Tortoises', 0, totalAnimals) m.addConstraint(eq(plus(c, t), totalAnimals)) m.addConstraint(eq(plus(mult(c, 2), mult(t, 4)), totalLegs)) s.read(m) def more = s.solve() while (more) { println "Found a solution:" [c, t].each { def v = s.getVar(it) if (v.val) println " $v.val * $v.name" } more = s.nextSolution() } Found a solution: 4 * Cranes 3 * Tortoises
  • 51. …Tortoises & Cranes: Choco import static choco.Choco.* import choco.cp.model.CPModel import choco.cp.solver.CPSolver import choco.kernel.model.variables.integer.IntegerVariable def m = new CPModel() def s = new CPSolver() def totalAnimals = 7 def totalLegs = 20 def c = makeIntVar('Cranes', 0, totalAnimals) def t = makeIntVar('Tortoises', 0, totalAnimals) IntegerVariable[] animals = [c, t] m.addConstraint(eq(plus(c, t), totalAnimals)) m.addConstraint(eq(scalar(animals, [2, 4] as int[]), totalLegs)) s.read(m) def more = s.solve() while (more) { println "Found a solution:" animals.each { def v = s.getVar(it) if (v.val) println " $v.val * $v.name" } more = s.nextSolution() } Slight variant using scalars. Well suited to scaling to more animals
  • 52. Tortoises & Cranes: Simplistic… import groovy.transform.Immutable import org.drools.builder.ResourceType import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newReaderResource def numAnimals = 7 def numLegs = 20 def kbuilder = newKnowledgeBuilder() kbuilder.add(newReaderResource(new StringReader(''' dialect "mvel" rule "deduce animal counts" when $crane : Crane( ) $tortoise : Tortoise( quantity + $crane.quantity == ''' + numAnimals + ''', quantity * numLegs + $crane.quantity * $crane.numLegs == ''' + numLegs + ''' ) then System.out.println( "Cranes " + $crane.getQuantity() ) System.out.println( "Tortoises " + $tortoise.getQuantity() ) end ''')), ResourceType.DRL) def kbase = newKnowledgeBase() kbase.addKnowledgePackages(kbuilder.knowledgePackages) def ksession = kbase.newStatefulKnowledgeSession()
  • 53. import groovy.transform.Immutable import org.drools.builder.ResourceType import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newReaderResource def numAnimals = 7 def numLegs = 20 def kbuilder = newKnowledgeBuilder() kbuilder.add(newReaderResource(new StringReader(''' dialect "mvel" rule "deduce animal counts" when $crane : Crane( ) $tortoise : Tortoise( quantity + $crane.quantity == ''' + numAnimals + ''', quantity * numLegs + $crane.quantity * $crane.numLegs == ''' + numLegs + ''' ) then System.out.println( "Cranes " + $crane.getQuantity() ) System.out.println( "Tortoises " + $tortoise.getQuantity() ) end ''')), ResourceType.DRL) def kbase = newKnowledgeBase() kbase.addKnowledgePackages(kbuilder.knowledgePackages) def ksession = kbase.newStatefulKnowledgeSession() …Tortoises & Cranes: Simplistic… … (numAnimals + 1).times { n -> if (numLegs.intdiv(Crane.numLegs) >= n) { ksession.insert(new Crane(n)) } if (numLegs.intdiv(Tortoise.numLegs) >= n) { ksession.insert(new Tortoise(n)) } } ksession.fireAllRules() ksession.dispose() @Immutable class Crane { static int numLegs = 2 int quantity } @Immutable class Tortoise { static int numLegs = 4 int quantity }
  • 54. import groovy.transform.Immutable import org.drools.builder.ResourceType import static org.drools.KnowledgeBaseFactory.newKnowledgeBase import static org.drools.builder.KnowledgeBuilderFactory.newKnowledgeBuilder import static org.drools.io.ResourceFactory.newReaderResource def numAnimals = 7 def numLegs = 20 def kbuilder = newKnowledgeBuilder() kbuilder.add(newReaderResource(new StringReader(''' dialect "mvel" rule "deduce animal counts" when $crane : Crane( ) $tortoise : Tortoise( quantity + $crane.quantity == ''' + numAnimals + ''', quantity * numLegs + $crane.quantity * $crane.numLegs == ''' + numLegs + ''' ) then System.out.println( "Cranes " + $crane.getQuantity() ) System.out.println( "Tortoises " + $tortoise.getQuantity() ) end ''')), ResourceType.DRL) def kbase = newKnowledgeBase() kbase.addKnowledgePackages(kbuilder.knowledgePackages) def ksession = kbase.newStatefulKnowledgeSession() …Tortoises & Cranes: Simplistic … (numAnimals + 1).times { n -> if (numLegs.intdiv(Crane.numLegs) >= n) { ksession.insert(new Crane(n)) } if (numLegs.intdiv(Tortoise.numLegs) >= n) { ksession.insert(new Tortoise(n)) } } ksession.fireAllRules() ksession.dispose() @Immutable class Crane { static int numLegs = 2 int quantity } @Immutable class Tortoise { static int numLegs = 4 int quantity } What is the impact of adding another kind of animal?
  • 55. Tortoises & Cranes: DSL… //@GrabResolver('https://repository.jboss.org/nexus/content/groups/public-jboss/') //@Grab('org.drools:knowledge-api:5.4.0.Final') //@Grab('org.drools:drools-compiler:5.4.0.Final') //@Grab('org.drools:drools-core:5.4.0.Final') //@Grab('com.sun.xml.bind:jaxb-xjc:2.2.5.jboss-1') //@GrabExclude('com.github.relaxng:relaxngDatatype') import groovy.transform.Field import org.drools.builder.ResourceType import static org.drools.KnowledgeBaseFactory.* import static org.drools.builder.KnowledgeBuilderFactory.* import static org.drools.io.ResourceFactory.newReaderResource class Solver { static main(Map animals, int totalAnimals, int totalLegs, ClassLoader loader) { def whenClauses = '' def thenClauses = '' def numAnimalsClause = '' def numLegsClause = '' def lastIndex = animals.size() - 1 animals.eachWithIndex { entry, index -> def key = entry.key def capKey = key.capitalize() whenClauses += ' $' + "$key : $capKey (" thenClauses += " System.out.println( "$capKey "" + ' + $' + key + '.getQuantity() )n' if (index != lastIndex) { numAnimalsClause += ' + $' + key + '.quantity' numLegsClause += ' + $' + key + '.quantity * $' + key + '.numLegs' whenClauses += ' )n' } else { whenClauses += 'n quantity' + numAnimalsClause + ' == ' + totalAnimals + ',' whenClauses += 'n quantity * numLegs' + numLegsClause + ' == ' + totalLegs whenClauses += 'n )n' } } …
  • 56. …Tortoises & Cranes: DSL… … def drl = ''' dialect "mvel" rule "deduce animal counts" when ''' + whenClauses + ''' then ''' + thenClauses + '''end ''' def kbuilderConf = newKnowledgeBuilderConfiguration(null, loader) def kbuilder = newKnowledgeBuilder(kbuilderConf) kbuilder.add(newReaderResource(new StringReader(drl)), ResourceType.DRL) def kbaseConf = newKnowledgeBaseConfiguration(null, loader) def kbase = newKnowledgeBase(kbaseConf) kbase.addKnowledgePackages(kbuilder.knowledgePackages) def ksession = kbase.newStatefulKnowledgeSession() (totalAnimals + 1).times { n -> animals.each { key, val -> def capKey = key.capitalize() Class animal = loader.loadClass(capKey) if (totalLegs.intdiv(animal.numLegs) >= n) { ksession.insert(animal.newInstance(n)) } } } ksession.fireAllRules() ksession.dispose() } } …
  • 57. …Tortoises & Cranes: DSL… … @Field animalProps = [:] def props = [:] def methodMissing(String name, _have) { new AnimalHolder(animals: animalProps, name: name) } def propertyMissing(String name) { name } class ThereHolder { def props def methodMissing(String name, args) { props['total' + args[0].capitalize()] = name.toInteger() } } class AnimalHolder { def animals, name def methodMissing(String number, args) { animals[name] = number.toInteger() } } def there = { _are -> new ThereHolder(props: props) }
  • 58. …Tortoises & Cranes: DSL … cranes have 2 legs tortoises have 4 legs //millipedes have 1000 legs there are 7 animals //there are 8 animals there are 20 legs //there are 1020 legs new GroovyShell([animals: animalProps] as Binding).evaluate( animalProps.collect { key, val -> def capKey = key.capitalize() """ @groovy.transform.Immutable class $capKey { static int numLegs = $val int quantity } """ }.join('n') + "Solver.main(animals, $props.totalAnimals, $props.totalLegs, getClass().classLoader)" ) Cranes 4 Tortoises 3 Cranes 4 Tortoises 3 Millipedes 1
  • 59. Topics • Introduction to DSLs • Introduction to Groovy • DSLs in Groovy • Why Groovy? • Tortoise & Crane Example Einstein’s Riddle • Further Discussion • More Info ©ASERT2006-2013
  • 60. Einstein’s Riddle… • Wikipedia: The zebra puzzle is a well- known logic puzzle – It is often called Einstein's Puzzle or Einstein's Riddle because it is said to have been invented by Albert Einstein as a boy, with the claim that Einstein said “… only 2 percent of the world's population can solve it.” – The puzzle is also sometimes attributed to Lewis Carroll. However, there is no known evidence for Einstein's or Carroll's authorship; and the original puzzle cited mentions brands of cigarette, such as Kools, that did not exist during Carroll's lifetime or Einstein's boyhood ©ASERT2006-2013
  • 61. …Einstein’s Riddle ©ASERT2006-2013 • Some premises: – The British person lives in the red house – The Swede keeps dogs as pets – The Dane drinks tea – The green house is on the left of the white house – The green homeowner drinks coffee – The man who smokes Pall Mall keeps birds – The owner of the yellow house smokes Dunhill – The man living in the center house drinks milk – The Norwegian lives in the first house – The man who smokes Blend lives next to the one who keeps cats – The man who keeps the horse lives next to the man who smokes Dunhill – The man who smokes Bluemaster drinks beer – The German smokes Prince – The Norwegian lives next to the blue house – The man who smokes Blend has a neighbor who drinks water • And a question: – Who owns the fish?
  • 62. Einstein’s Riddle : Prolog ©ASERT2006-2013 % from http://www.baptiste-wicht.com/2010/09/solve-einsteins-riddle-using- prolog % Preliminary definitions persons(0, []) :- !. persons(N, [(_Men,_Color,_Drink,_Smoke,_Animal)|T]) :- N1 is N-1, persons(N1,T). person(1, [H|_], H) :- !. person(N, [_|T], R) :- N1 is N-1, person(N1, T, R). % The Brit lives in a red house hint1([(brit,red,_, _, _)|_]). hint1([_|T]) :- hint1(T). % The Swede keeps dogs as pets hint2([(swede,_,_,_,dog)|_]). hint2([_|T]) :- hint2(T). % The Dane drinks tea hint3([(dane,_,tea,_,_)|_]). hint3([_|T]) :- hint3(T). % The Green house is on the left of the White house hint4([(_,green,_,_,_),(_,white,_,_,_)|_]). hint4([_|T]) :- hint4(T). % The owner of the Green house drinks coffee.
  • 63. Einstein’s Riddle : Polyglot ©ASERT2006-2013 @GrabResolver('http://dev.inf.unideb.hu:8090/archiva/repository/internal') //@Grab('jlog:jlogic-debug:1.3.6') @Grab('org.prolog4j:prolog4j-api:0.2.0') // uncomment one of the next three lines //@Grab('org.prolog4j:prolog4j-jlog:0.2.0') @Grab('org.prolog4j:prolog4j-tuprolog:0.2.0') //@Grab('org.prolog4j:prolog4j-jtrolog:0.2.0') import org.prolog4j.* def p = ProverFactory.prover p.addTheory(new File('/GroovyExamples/tuProlog/src/einstein.pl').text) def sol = p.solve("solution(Persons).") //println sol.solution.get('Persons') // jlog to avoid converter println sol.get('Persons') // jtrolog/tuProlog
  • 64. Einstein’s Riddle : Polyglot w/ DSL… ©ASERT2006-2013 // define some domain classes and objects enum Pet { dog, cat, bird, fish, horse } enum Color { green, white, red, blue, yellow } enum Smoke { dunhill, blends, pallmall, prince, bluemaster } enum Drink { water, tea, milk, coffee, beer } enum Nationality { Norwegian, Dane, Brit, German, Swede } dogs = dog; birds = bird; cats = cat; horses = horse a = owner = house = the = abode = person = man = is = to = side = next = who = different = 'ignored' // some preliminary definitions p = ProverFactory.prover hintNum = 1 p.addTheory(''' persons(0, []) :- !. persons(N, [(_Men,_Color,_Drink,_Smoke,_Animal)|T]) :- N1 is N-1, persons(N1,T). person(1, [H|_], H) :- !. person(N, [_|T], R) :- N1 is N-1, person(N1, T, R). ''')
  • 65. …Einstein’s Riddle : Polyglot w/ DSL… ©ASERT2006-2013 // define some helper methods (our interface to prolog) def addPairHint(Map m) { def from = m.from?.toString()?.toLowerCase() p.addTheory(""" hint$hintNum([(${from ?: '_'},${m.color ?: '_'},${m.drink ?: '_'},${m.smoke ?: '_'},${m.pet ?: '_'})|_]). hint$hintNum([_|T]) :- hint$hintNum(T). """) hintNum++ } def addPositionHint(Map m, int pos) { def from = m.from?.toString()?.toLowerCase() p.addTheory(""" hint$hintNum(Persons) :- person($pos, Persons, (${from ?: '_'},${m.color ?: '_'},${m.drink ?: '_'},${m.smoke ?: '_'},${m.pet ?: '_'})). """) hintNum++ } def addToLeftHint(Map left, Map right) { p.addTheory(""" hint$hintNum([(_,$left.color,_,_,_),(_,$right.color,_,_,_)|_]). hint$hintNum([_|T]) :- hint$hintNum(T). """) hintNum++ } ...
  • 66. …Einstein’s Riddle : Polyglot w/ DSL… ©ASERT2006-2013 // now implement DSL in terms of helper methods def the(Nationality n) { def ctx = [from:n] [ drinks: { d -> addPairHint(ctx + [drink:d]) }, smokes: { s -> addPairHint(ctx + [smoke:s]) }, keeps: { p -> addPairHint(ctx + [pet:p]) }, rears: { p -> addPairHint(ctx + [pet:p]) }, owns:{ _the -> [first:{ house -> addPositionHint(ctx, 1) }] }, has:{ _a -> [pet: { a -> addPairHint(ctx + [pet:a]) }] + Color.values().collectEntries{ c -> [c.toString(), { _dummy -> addPairHint(ctx + [color:c]) } ] } }, lives: { _next -> [to: { _the -> Color.values().collectEntries{ c -> [c.toString(), { _dummy -> addNeighbourHint(ctx, [color:c]) } ] } }]} ] } ...
  • 67. …Einstein’s Riddle : Polyglot w/ DSL… ©ASERT2006-2013 // now define the DSL the man from the centre house drinks milk the Norwegian owns the first house the Dane drinks tea the German smokes prince the Swede keeps dogs // alternate ending: has a pet dog the Brit has a red house // alternate ending: red abode the owner of the green house drinks coffee the owner of the yellow house smokes dunhill the person known to smoke pallmall rears birds // other ending: keeps birds the man known to smoke bluemaster drinks beer the green house is on the left side of the white house the man known to smoke blends lives next to the one who keeps cats the man known to keep horses lives next to the man who smokes dunhill the man known to smoke blends lives next to the one who drinks water the Norwegian lives next to the blue house
  • 68. …Einstein’s Riddle : Polyglot w/ DSL… ©ASERT2006-2013 // now implement DSL in terms of helper methods def the(Nationality n) { def ctx = [from:n] [ drinks: { d -> addPairHint(ctx + [drink:d]) }, smokes: { s -> addPairHint(ctx + [smoke:s]) }, keeps: { p -> addPairHint(ctx + [pet:p]) }, ... ] } ... the German smokes prince the(German).smokes(prince) n = German ctx = [from: German] [drinks: …, smokes: { s -> addPairHint([from: German, smoke: s]) }, keeps: …, … ] addPairHint([from: German, smoke: prince])
  • 69. …Einstein’s Riddle : Polyglot w/ DSL… • Some parts of our DSL are automatically statically inferred, e.g. typing ‘bl’ and then asking for completion yields: • But other parts are not known, e.g. the word ‘house’ in the fragment below: ©ASERT2006-2013 ‘house’ is key for a Map and could be any value
  • 70. …Einstein’s Riddle : Polyglot w/ DSL ©ASERT2006-2013 class HousePlaceHolder { def c1, script def house(_is) { [on: { _the -> [left: { _side -> [of: { __the -> Color.values().collectEntries { c2 -> [c2.toString(), { _dummy -> script.addToLeftHint( [color: c1], [color: c2] )}]} }]}]}] } } def the(Color c1) { new HousePlaceHolder(c1:c1, script:this) } def the(Color c1) {[ house: { _is -> [on: { _the -> [left: { _side -> [of: { __the -> Color.values().collectEntries{ c2 -> [c2.toString(), { _dummy -> addToLeftHint([color:c1], [color:c2]) }]} }]}]}]} ]} ‘house’ is now understood We can choose to introduce additional static typing information into our DSL implementation or ‘teach’ our IDE about or DSL.
  • 71. Einstein’s Riddle : Choco DSL… ©ASERT2006-2013 //@GrabResolver('http://www.emn.fr/z-info/choco-repo/mvn/repository') //@Grab('choco:choco-solver:2.1.5') import static choco.Choco.* import choco.kernel.model.variables.integer.* import groovy.transform.Field enum Pet { dog, cat, bird, fish, horse } enum Color { green, white, red, blue, yellow } enum Sport { baseball, volleyball, football, hockey, tennis } enum Drink { water, tea, milk, coffee, beer } enum Nationality { Norwegian, Dane, Briton, German, Swede } import static Pet.* import static Color.* import static Sport.* import static Drink.* import static Nationality.* // define logic solver data structures num = 5 center = 2 first = 0 println "Solving Einstein's Riddle:" …
  • 72. …Einstein’s Riddle : Choco DSL… ©ASERT2006-2013 … @Field m = new choco.cp.model.CPModel() def s = new choco.cp.solver.CPSolver() choco.Choco.metaClass.static.eq = { c, v -> delegate.eq(c, v.ordinal()) } def makeEnumVar(st, arr) { choco.Choco.makeIntVar(st, 0, arr.size()-1, choco.Options.V_ENUM) } pets = new IntegerVariable[num] colors = new IntegerVariable[num] plays = new IntegerVariable[num] drinks = new IntegerVariable[num] nations = new IntegerVariable[num] (0..<num).each { i -> pets[i] = makeEnumVar("pet$i", pets) colors[i] = makeEnumVar("color$i", colors) plays[i] = makeEnumVar("plays$i", plays) drinks[i] = makeEnumVar("drink$i", drinks) nations[i] = makeEnumVar("nation$i", nations) } def pretty(s, c, arr, i) { c.values().find{ it.ordinal() == s.getVar(arr[i])?.value } } …
  • 73. …Einstein’s Riddle : Choco DSL… ©ASERT2006-2013 … // define DSL (simplistic non-refactored version) def neighbours(var1, val1, var2, val2) { m.addConstraint and( ifOnlyIf(eq(var1[0], val1), eq(var2[1], val2)), implies(eq(var1[1], val1), or(eq(var2[0], val2), eq(var2[2], val2))), implies(eq(var1[2], val1), or(eq(var2[1], val2), eq(var2[3], val2))), implies(eq(var1[3], val1), or(eq(var2[2], val2), eq(var2[4], val2))), ifOnlyIf(eq(var1[4], val1), eq(var2[3], val2)) ) } iff = { e1, c1, e2, c2 -> m.addConstraint and(*(0..<num).collect{ ifOnlyIf(eq(e1[it], c1), eq(e2[it], c2)) }) } isEq = { a, b -> m.addConstraint eq(a, b) } dogs = dog; birds = bird; cats = cat; horses = horse a = owner = house = the = abode = person = man = to = is = side = next = who = different = 'ignored' …
  • 74. …Einstein’s Riddle : Choco DSL… ©ASERT2006-2013 … // define the DSL in terms of DSL implementation def the(Nationality n) { def ctx = [nations, n] [ drinks:iff.curry(*ctx, drinks), plays:iff.curry(*ctx, plays), keeps:iff.curry(*ctx, pets), rears:iff.curry(*ctx, pets), owns:{ _the -> [first:{ house -> isEq(nations[first], n)}] }, has:{ _a -> [pet:iff.curry(*ctx, pets)] + Color.values().collectEntries{ c -> [c.toString(), { _dummy -> iff(*ctx, colors, c) } ] } }, lives: { _next -> [to: { _the -> Color.values().collectEntries{ c -> [c.toString(), { _dummy -> neighbours(*ctx, colors, c) } ] } }]} ] } …
  • 75. …Einstein’s Riddle : Choco DSL… ©ASERT2006-2013 … def the(Color c1) {[ house: { _is -> [on: { _the -> [left: { _side -> [of: { __the -> Color.values().collectEntries{ c2 -> [c2.toString(), { _dummy -> m.addConstraint and(*(1..<num).collect{ ifOnlyIf(eq(colors[it-1], c1), eq(colors[it], c2)) }) }]} }]}]}]} ]} def the(String _dummy) {[ of:{ _the -> Color.values().collectEntries{ c -> [c.toString(), { _house -> [ drinks:iff.curry(colors, c, drinks), plays:iff.curry(colors, c, plays) ] } ] } }, known: { _to -> [ play: { sport -> def ctx = [plays, sport] [ rears: iff.curry(*ctx, pets), keeps: iff.curry(*ctx, pets), drinks: iff.curry(*ctx, drinks), lives: { _next -> [to: { _the -> [one: { _who -> [ keeps: { pet -> neighbours(pets, pet, *ctx) }, drinks: { beverage -> neighbours(drinks, beverage, *ctx) } ]}]}]} ] }, …
  • 76. …Einstein’s Riddle : Choco DSL… ©ASERT2006-2013 … keep : { pet -> [ lives: { _next -> [to: { _the -> [man: { _who -> [ plays: { sport -> neighbours(pets, pet, plays, sport) } ]}]}]} ]} ]}, from: { _the -> [center: { house -> [drinks: { d -> isEq(drinks[center], d)}] }]} ]} def all(IntegerVariable[] var) { [are: { _different -> m.addConstraint allDifferent(var) } ] } …
  • 77. …Einstein’s Riddle : Choco DSL ©ASERT2006-2013 … // define rules all pets are different all colors are different all plays are different all drinks are different all nations are different the man from the center house drinks milk the Norwegian owns the first house the Dane drinks tea the German plays hockey the Swede keeps dogs // alternate ending: has a pet dog the Briton has a red house // alternate ending: red abode the owner of the green house drinks coffee the owner of the yellow house plays baseball the person known to play football rears birds // alternate ending: keeps birds the man known to play tennis drinks beer the green house is on the left side of the white house the man known to play volleyball lives next to the one who keeps cats the man known to keep horses lives next to the man who plays baseball the man known to play volleyball lives next to the one who drinks water the Norwegian lives next to the blue house …
  • 78. …Einstein’s Riddle : Choco… ©ASERT2006-2013 … // invoke logic solver s.read(m) def more = s.solve() while (more) { for (i in 0..<num) { print 'The ' + pretty(s, Nationality, nations, i) print ' has a pet ' + pretty(s, Pet, pets, i) print ' plays ' + pretty(s, Sport, plays, i) print ' drinks ' + pretty(s, Drink, drinks, i) println ' and lives in a ' + pretty(s, Color, colors, i) + ' house' } more = s.nextSolution() }
  • 79. Einstein’s Riddle : Output ©ASERT2006-2013 Solving Einstein's Riddle: The Norwegian has a pet cat plays baseball drinks water and lives in a yellow house The Dane has a pet horse plays volleyball drinks tea and lives in a blue house The Briton has a pet bird plays football drinks milk and lives in a red house The German has a pet fish plays hockey drinks coffee and lives in a green house The Swede has a pet dog plays tennis drinks beer and lives in a white house
  • 80. Topics • Introduction to DSLs • Introduction to Groovy • DSLs in Groovy • Why Groovy? • Tortoise & Crane Example • Einstein’s Riddle Further Discussion • More Info ©ASERT2006-2013
  • 81. Discussion points • Choosing granularity • Choosing the level of dynamic/static typing • Multi-paradigm solutions • Capturing Rule Design Patterns using AST transforms
  • 82. Granularity Solve manners2009 Neighbours must share a hobby Neighbours are of a different gender There should be 2 doctors at each table Each doctor at a table should be a different kind ... The Guest at position 2 on table 1 should have a different gender to the Guest at position 1 The Guest at position 2 on table 1 should have a different gender to the Guest at position 3 ...
  • 83. Typing… • Dynamic • Traditional Static Typing • Stronger levels of Static Typing
  • 84. …Typing… import groovy.transform.TypeChecked import experimental.SprintfTypeCheckingVisitor @TypeChecked(visitor=SprintfTypeCheckingVisitor) void main() { sprintf('%s will turn %d on %tF', 'John', new Date(), 21) } [Static type checking] - Parameter types didn't match types expected from the format String: For placeholder 2 [%d] expected 'int' but was 'java.util.Date' For placeholder 3 [%tF] expected 'java.util.Date' but was 'int' sprintf has an Object varargs parameter, hence not normally amenable to further static checking but for constant Strings we can do better using a custom type checking plugin.
  • 85. …Typing… import groovy.transform.TypeChecked import tictactoe.* Import static tictactoe.Position.* @TypeChecked(visitor=TicTacToeTypeVisitor) void main() { Board.empty().move(NW).move(C).move(W).move(SW).move(SE) } package tictactoe enum Position { NW, N, NE, W, C, E, SW, S, SE } class Board { static Board empty() { new Board() } Board move(Position p) { this } }
  • 86. …Typing import groovy.transform.TypeChecked import tictactoe.* Import static tictactoe.Position.* @TypeChecked(visitor=TicTacToeTypeVisitor) void main() { Board.empty().move(NW).move(C).move(W).move(SW).move(SE) } package tictactoe enum Position { NW, N, NE, W, C, E, SW, S, SE } [Static type checking] - Attempt to call suboptimal move SE not allowed [HINT: try NE] Custom type checker which fails compilation if programmer attempts to code a suboptimal solution. Where suboptimal means doesn’t agree with what is returned by a minimax, alpha-beta pruning, iterative deepening solving engine.
  • 87. Multi-paradigm solutions • Imperative • Functional – Leveraging immutable data structures – Persistent data structures – Higher-order functions • Rules-based • Concurrency, e.g. Gpars – Data Parallelism: Map, Reduce – DataFlow – Others: Fork Join, Actors
  • 88. Using compile-time Metaprogramming • Powerful mechanism – As illustrated by GOF examples – @Immutable, @Delegate and others • Rich area for further research – Explore whether rules design patterns can be readily embodied within AST transforms
  • 89. Topics • Introduction to DSLs • Introduction to Groovy • DSLs in Groovy • Why Groovy? • Tortoise & Crane Example • Einstein’s Riddle • Further Discussion More Info ©ASERT2006-2013
  • 90. More Information: URLs • Groovy – http://groovy.codehaus.org/ • Groovy DSL talk in general – http://www.slideshare.net/glaforge/groovy-domain-specific- languages-springone2gx-2013 • Groovy & Other Paradigms – http://www.slideshare.net/paulk_asert/concurrency-with-gpars – http://www.slideshare.net/paulk_asert/functional-groovy • Drools Expert & Planner – http://www.jboss.org/drools/ • Choco – http://www.emn.fr/z-info/choco-solver/
  • 91. More Information: Groovy in Action 2ed Contains a chapter on DSLs!