groovy rules
-
Upload
paul-king -
Category
Technology
-
view
7.164 -
download
6
description
Transcript of groovy rules
![Page 1: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/1.jpg)
© A
SE
RT
2006
-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
![Page 2: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/2.jpg)
Topics
Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
© A
SE
RT
2006-2
013
![Page 3: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/3.jpg)
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
© A
SE
RT
2006-2
013
![Page 4: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/4.jpg)
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
![Page 5: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/5.jpg)
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
![Page 6: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/6.jpg)
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
© A
SE
RT
2006-2
013
![Page 7: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/7.jpg)
Topics
• Introduction to DSLs
Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
© A
SE
RT
2006-2
013
![Page 8: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/8.jpg)
Java code for list manipulation
© A
SE
RT
2006-2
013
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
![Page 9: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/9.jpg)
Groovy code for list manipulation
© A
SE
RT
2006-2
013
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
![Page 10: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/10.jpg)
Some Java Boilerplate identified
© A
SE
RT
2006-2
013
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?
![Page 11: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/11.jpg)
Java Boilerplate removed
© A
SE
RT
2006-2
013
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) }
![Page 12: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/12.jpg)
More Java Boilerplate identified
© A
SE
RT
2006-2
013
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?
![Page 13: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/13.jpg)
Boilerplate removed = nicer Groovy version
© A
SE
RT
2006-2
013
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:
![Page 14: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/14.jpg)
Or Groovy DSL version if required
© A
SE
RT
2006-2
013
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
![Page 15: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/15.jpg)
Closures
© A
SE
RT
2006-2
013
int twice(int arg) { arg * 2 } def triple = { int arg -> arg * 3 } println twice(3) // => 6 println triple(3) // => 9
![Page 16: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/16.jpg)
Grapes / Grab: Google collections
© A
SE
RT
2006-2
013
@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'
![Page 17: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/17.jpg)
© A
SE
RT
2006-2
011
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
![Page 18: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/18.jpg)
Topics
• Introduction to DSLs
• Introduction to Groovy
DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
© A
SE
RT
2006-2
013
![Page 19: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/19.jpg)
DSL example...
© A
SE
RT
2006-2
013
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
![Page 20: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/20.jpg)
…DSL example...
© A
SE
RT
2006-2
013
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
![Page 21: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/21.jpg)
…DSL example...
© A
SE
RT
2006-2
013
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
![Page 22: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/22.jpg)
…DSL example...
© A
SE
RT
2006-2
013
please show the square_root of 100
![Page 23: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/23.jpg)
…DSL example...
© A
SE
RT
2006-2
013
please show the square_root of 100
please(show).the(square_root).of(100)
![Page 24: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/24.jpg)
…DSL example...
© A
SE
RT
2006-2
013
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)
![Page 25: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/25.jpg)
…DSL example...
© A
SE
RT
2006-2
013
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
![Page 26: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/26.jpg)
...DSL example...
© A
SE
RT
2006-2
013
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
![Page 27: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/27.jpg)
...DSL example...
© A
SE
RT
2006-2
013
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 …
![Page 28: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/28.jpg)
...DSL example
© A
SE
RT
2006-2
013
// 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
![Page 29: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/29.jpg)
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
© A
SE
RT
2006-2
013
![Page 30: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/30.jpg)
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
![Page 31: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/31.jpg)
Compile-time Metaprogramming
© A
SE
RT
2006-2
013
Transformation
![Page 32: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/32.jpg)
@Immutable...
• Java Immutable Class – As per Joshua Bloch
Effective Java
© A
SE
RT
2006-2
013
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 + ")"; } }
![Page 33: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/33.jpg)
...@Immutable...
• Java Immutable Class – As per Joshua Bloch
Effective Java
© A
SE
RT
2006-2
013
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
![Page 34: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/34.jpg)
...@Immutable
© A
SE
RT
2006-2
013
@Immutable class Person { String first, last }
![Page 35: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/35.jpg)
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
![Page 36: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/36.jpg)
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
![Page 37: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/37.jpg)
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
![Page 38: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/38.jpg)
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; } } }
![Page 39: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/39.jpg)
…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 }
![Page 40: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/40.jpg)
…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 }
![Page 41: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/41.jpg)
…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 }
![Page 42: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/42.jpg)
@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() // ...
![Page 43: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/43.jpg)
…@Grab…
> groovy -classpath C:\Projects\GroovyProblemSolvers\out\production\DroolsExpert; C:\Users\paulk\.groovy\grapes\org.drools\drools-compiler\jars\drools-compiler-5.3.3.Final.jar; C:\Users\paulk\.groovy\grapes\org.antlr\antlr-runtime\jars\antlr-runtime-3.3.jar; C:\Users\paulk\.groovy\grapes\org.antlr\antlr\jars\antlr-3.3.jar; C:\Users\paulk\.groovy\grapes\org.antlr\stringtemplate\jars\stringtemplate-3.2.1.jar; C:\Users\paulk\.groovy\grapes\antlr\antlr\jars\antlr-2.7.7.jar; C:\Users\paulk\.groovy\grapes\org.eclipse.jdt.core.compiler\ecj\jars\ecj-3.5.1.jar; C:\Users\paulk\.groovy\grapes\org.mvel\mvel2\jars\mvel2-2.1.0.drools16.jar; C:\Users\paulk\.groovy\grapes\com.sun.xml.bind\jaxb-xjc\jars\jaxb-xjc-2.2.5.jboss-1.jar; C:\Users\paulk\.groovy\grapes\com.sun.xml.bind\jaxb-impl\jars\jaxb-impl-2.2.5.jboss-1.jar; C:\Users\paulk\.groovy\grapes\javax.xml.bind\jaxb-api\jars\jaxb-api-2.2.6.jar; C:\Users\paulk\.groovy\grapes\com.sun.istack\istack-commons-runtime\jars\istack-commons-runtime-2.6.1.jar; C:\Users\paulk\.groovy\grapes\javax.xml.stream\stax-api\jars\stax-api-1.0-2.jar; C:\Users\paulk\.groovy\grapes\javax.activation\activation\jars\activation-1.1.jar; C:\Users\paulk\.groovy\grapes\com.sun.xml.txw2\txw2\jars\txw2-20110809.jar; C:\Users\paulk\.groovy\grapes\relaxngDatatype\relaxngDatatype\jars\relaxngDatatype-20020414.jar; C:\Users\paulk\.groovy\grapes\com.sun.codemodel\codemodel\jars\codemodel-2.6.jar; C:\Users\paulk\.groovy\grapes\com.sun.xml.dtd-parser\dtd-parser\jars\dtd-parser-1.1.jboss-1.jar; C:\Users\paulk\.groovy\grapes\com.sun.istack\istack-commons-tools\jars\istack-commons-tools-2.6.1.jar; C:\Users\paulk\.groovy\grapes\org.apache.ant\ant\jars\ant-1.7.0.jar; C:\Users\paulk\.groovy\grapes\org.apache.ant\ant-launcher\jars\ant-launcher-1.7.0.jar; C:\Users\paulk\.groovy\grapes\org.kohsuke.rngom\rngom\jars\rngom-201103.jboss-1.jar; C:\Users\paulk\.groovy\grapes\com.sun.xsom\xsom\jars\xsom-20110809.jar; C:\Users\paulk\.groovy\grapes\xml-resolver\xml-resolver\jars\xml-resolver-1.1.jar; C:\Users\paulk\.groovy\grapes\org.drools\drools-core\jars\drools-core-5.3.3.Final.jar; C:\Users\paulk\.groovy\grapes\org.drools\knowledge-api\jars\knowledge-api-5.3.3.Final.jar; C:\Projects\GroovyProblemSolvers\DroolsExpert\src\resources GolfExample.groovy
Or you can precompile: > groovyc -classpath ... GolfExample.groovy > jar ... Then use groovy or java commands to run.
Old school
![Page 44: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/44.jpg)
…@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
![Page 45: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/45.jpg)
…@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() // ...
![Page 46: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/46.jpg)
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 ) // ...
![Page 47: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/47.jpg)
…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
![Page 48: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/48.jpg)
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
• More Info
© A
SE
RT
2006-2
013
![Page 49: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/49.jpg)
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
![Page 50: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/50.jpg)
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
![Page 51: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/51.jpg)
…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
![Page 52: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/52.jpg)
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() …
![Page 53: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/53.jpg)
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 }
![Page 54: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/54.jpg)
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?
![Page 55: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/55.jpg)
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' } } …
![Page 56: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/56.jpg)
…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() } } …
![Page 57: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/57.jpg)
…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) }
![Page 58: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/58.jpg)
…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
![Page 59: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/59.jpg)
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
Einstein’s Riddle
• Further Discussion
• More Info
© A
SE
RT
2006-2
013
![Page 60: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/60.jpg)
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
© A
SE
RT
2006-2
013
![Page 61: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/61.jpg)
…Einstein’s Riddle
© A
SE
RT
2006-2
013
• 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?
![Page 62: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/62.jpg)
Einstein’s Riddle : Prolog
© A
SE
RT
2006-2
013
% 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. hint5([(_,green,coffee,_,_)|_]). hint5([_|T]) :- hint5(T). ...
![Page 63: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/63.jpg)
Einstein’s Riddle : Polyglot
© A
SE
RT
2006-2
013
@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
![Page 64: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/64.jpg)
Einstein’s Riddle : Polyglot w/ DSL…
© A
SE
RT
2006-2
013
// 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). ''')
![Page 65: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/65.jpg)
…Einstein’s Riddle : Polyglot w/ DSL…
© A
SE
RT
2006-2
013
// 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++ } ...
![Page 66: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/66.jpg)
…Einstein’s Riddle : Polyglot w/ DSL…
© A
SE
RT
2006-2
013
// 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]) } ] } }]} ] } ...
![Page 67: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/67.jpg)
…Einstein’s Riddle : Polyglot w/ DSL…
© A
SE
RT
2006-2
013
// 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
![Page 68: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/68.jpg)
…Einstein’s Riddle : Polyglot w/ DSL…
© A
SE
RT
2006-2
013
// 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])
![Page 69: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/69.jpg)
…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:
© A
SE
RT
2006-2
013
‘house’ is key for a Map and could be any value
![Page 70: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/70.jpg)
…Einstein’s Riddle : Polyglot w/ DSL
© A
SE
RT
2006-2
013
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.
![Page 71: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/71.jpg)
Einstein’s Riddle : Choco DSL…
© A
SE
RT
2006-2
013
//@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:" …
![Page 72: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/72.jpg)
…Einstein’s Riddle : Choco DSL…
© A
SE
RT
2006-2
013
… @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 } } …
![Page 73: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/73.jpg)
…Einstein’s Riddle : Choco DSL…
© A
SE
RT
2006-2
013
… // 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' …
![Page 74: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/74.jpg)
…Einstein’s Riddle : Choco DSL…
© A
SE
RT
2006-2
013
… // 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) } ] } }]} ] } …
![Page 75: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/75.jpg)
…Einstein’s Riddle : Choco DSL…
© A
SE
RT
2006-2
013
… 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) } ]}]}]} ] }, …
![Page 76: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/76.jpg)
…Einstein’s Riddle : Choco DSL…
© A
SE
RT
2006-2
013
… 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) } ] } …
![Page 77: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/77.jpg)
…Einstein’s Riddle : Choco DSL
© A
SE
RT
2006-2
013
… // 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 …
![Page 78: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/78.jpg)
…Einstein’s Riddle : Choco…
© A
SE
RT
2006-2
013
… // 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() }
![Page 79: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/79.jpg)
Einstein’s Riddle : Output
© A
SE
RT
2006-2
013
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
![Page 80: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/80.jpg)
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
Further Discussion
• More Info
© A
SE
RT
2006-2
013
![Page 81: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/81.jpg)
Discussion points
• Choosing granularity
• Choosing the level of dynamic/static typing
• Multi-paradigm solutions
• Capturing Rule Design Patterns using AST
transforms
![Page 82: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/82.jpg)
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 ...
![Page 83: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/83.jpg)
Typing…
• Dynamic
• Traditional Static Typing
• Stronger levels of Static Typing
![Page 84: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/84.jpg)
…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.
![Page 85: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/85.jpg)
…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 } }
![Page 86: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/86.jpg)
…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.
![Page 87: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/87.jpg)
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
![Page 88: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/88.jpg)
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
![Page 89: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/89.jpg)
Topics
• Introduction to DSLs
• Introduction to Groovy
• DSLs in Groovy
• Why Groovy?
• Tortoise & Crane Example
• Einstein’s Riddle
• Further Discussion
More Info
© A
SE
RT
2006-2
013
![Page 90: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/90.jpg)
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/
![Page 91: groovy rules](https://reader030.fdocuments.in/reader030/viewer/2022013118/553890ad550346722e8b484f/html5/thumbnails/91.jpg)
More Information: Groovy in Action 2ed
Contains a
chapter on
DSLs!