internal dsls

95
internal DSLs Zef Hemel Tuesday, May 18, 2010

Upload: zefhemel

Post on 10-May-2015

2.212 views

Category:

Technology


3 download

DESCRIPTION

My lecture about internal DSL implementation techniques.

TRANSCRIPT

Page 1: Internal DSLs

internal DSLsZef Hemel

Tuesday, May 18, 2010

Page 2: Internal DSLs

Internal DSLs are particular ways of using a host language to give the host language the feel of a particular language.

Martin Fowler

Tuesday, May 18, 2010

Page 3: Internal DSLs

Internal DSLs are particular ways of using a host language to give the host language the feel of a particular language.

Martin Fowler

ab

Tuesday, May 18, 2010

Page 4: Internal DSLs

fluent interfaces

flexible syntax

reflection

macros

method missing

Tuesday, May 18, 2010

Page 5: Internal DSLs

Tuesday, May 18, 2010

Page 6: Internal DSLs

fluent interfaces

Tuesday, May 18, 2010

Page 7: Internal DSLs

Order o = new Order();Product p1 = new Product(1,Product.find(“Billy”));o.addProduct(p1);Product p2 = new Product(2,Product.find(”Janso"));o.addProduct(p2);Product p3 = new Product(4,Product.find(“Traby"));o.addProduct(p3);o.setPriorityRush(true);customer.addOrder(o);

http://www.st.ewi.tudelft.nl/~bouwers/main/slides/2008jspring.pdf

Tuesday, May 18, 2010

Page 8: Internal DSLs

customer.newOrder() .with(1, "Billy") .with(2, "Janso") .with(4, "Traby") .priorityRush() .done();

Tuesday, May 18, 2010

Page 9: Internal DSLs

public class Customer { ... public OrderBuilder newOrder() { return new OrderBuilder(this); }}

Tuesday, May 18, 2010

Page 10: Internal DSLs

public class OrderBuilder { // ...

public OrderBuilder(Customer customer) { this.customer = customer; this.order = new Order(); }

public OrderBuilder with(int id, String name) { order.addProduct(new Product(id, name)); return this; }

public OrderBuilder priorityRush() { order.setPriorityRush(true); return this; }

public void done() { customer.addOrder(this.order); }}

Tuesday, May 18, 2010

Page 11: Internal DSLs

public class OrderBuilder { // ...

public OrderBuilder(Customer customer) { this.customer = customer; this.order = new Order(); }

public OrderBuilder with(int id, String name) { order.addProduct(new Product(id, name)); return this; }

public OrderBuilder priorityRush() { order.setPriorityRush(true); return this; }

public void done() { customer.addOrder(this.order); }}

Tuesday, May 18, 2010

Page 12: Internal DSLs

flexible syntax

Tuesday, May 18, 2010

Page 13: Internal DSLs

header("Add entry")form { table { row { col { text("Your name:") } col { newEntry.name = input(newEntry.name) } } row { col { text("Your message:") } col { newEntry.text = inputText(newEntry.text) } } } button("Post") { newEntry.save() goto(Home()) }}

Tuesday, May 18, 2010

Page 14: Internal DSLs

in Scala

Tuesday, May 18, 2010

Page 15: Internal DSLs

case class Home() extends Page { def ui { header("Welcome to my guestbook!") section { header("All entries") list { for (e <- cache("entries", Entry.all)) { listitem { form { text(e.name) text(": ") text(e.text) button("Delete") { e.delete() goto(Home()) } } } } } } }}

Tuesday, May 18, 2010

Page 16: Internal DSLs

case class Home() extends Page { def ui { header("Welcome to my guestbook!") section { entries } }

def entries { header("All entries") list { for (e <- cache("entries", Entry.all)) { listitem { form { text(e.name) text(": ") text(e.text) button("Delete") { e.delete() goto(Home()) } } } } } }}

Tuesday, May 18, 2010

Page 17: Internal DSLs

object DefaultStyle extends Style { block("headerblock") >> header { fontsize = 30 pt; width = 100 percent; bgcolor = "#eeeeee"; } section >> header { color = "#0c0ccc"; } body { fontfamily = "Helvetica, Arial, Verdana, sans-serif" }}

Tuesday, May 18, 2010

Page 18: Internal DSLs

object DefaultStyle extends Style { block("headerblock").>>(header { fontsize = 30 pt; width = 100 percent; bgcolor = "#eeeeee"; }) section.>>(header { color = "#0c0ccc"; }) body { fontfamily = "Helvetica, Arial, Verdana, sans-serif" }}

Tuesday, May 18, 2010

Page 19: Internal DSLs

object DefaultStyle extends Style { block("headerblock").>>(header(() => { fontsize = 30 pt; width = 100 percent; bgcolor = "#eeeeee"; })) section.>>(header(() => { color = "#0c0ccc"; })) body(() => { fontfamily = "Helvetica, Arial, Verdana, sans-serif" })}

Tuesday, May 18, 2010

Page 20: Internal DSLs

a >> b == a.>>(b)

Tuesday, May 18, 2010

Page 21: Internal DSLs

section { header("All entries") ...}

Tuesday, May 18, 2010

Page 22: Internal DSLs

section(() => { header("All entries") ...})

Tuesday, May 18, 2010

Page 23: Internal DSLs

section(() => { header("All entries") ...})

def section(content : => Unit) { write("<div class='section'>") content write("</div>")}

Tuesday, May 18, 2010

Page 24: Internal DSLs

ruby

Tuesday, May 18, 2010

Page 25: Internal DSLs

create_table :posts do |t| t.string :name t.string :title t.text :contentend

Tuesday, May 18, 2010

Page 26: Internal DSLs

create_table(:posts,do |t| t.string(:name) t.string(:title) t.text (:content)end)

Tuesday, May 18, 2010

Page 27: Internal DSLs

class Post < ActiveRecord::Base validates_presence_of :name, :title validates_length_of :title, :minimum => 5end

Tuesday, May 18, 2010

Page 28: Internal DSLs

class Post < ActiveRecord::Base validates_presence_of(:name, :title) validates_length_of(:title, :minimum => 5)end

Tuesday, May 18, 2010

Page 29: Internal DSLs

method missing

Tuesday, May 18, 2010

Page 30: Internal DSLs

scala

Tuesday, May 18, 2010

Page 31: Internal DSLs

width = 100 percent;

Tuesday, May 18, 2010

Page 32: Internal DSLs

def width_=(w: UnitInt) { ...}

width = 100 percent;

Tuesday, May 18, 2010

Page 33: Internal DSLs

width_=(100 percent);

Tuesday, May 18, 2010

Page 34: Internal DSLs

width_=(100.percent);

Tuesday, May 18, 2010

Page 35: Internal DSLs

width_=(100.percent());

Tuesday, May 18, 2010

Page 36: Internal DSLs

implicit def int2UnitInt(i: Int) =new UnitIntWrapper(i)

Tuesday, May 18, 2010

Page 37: Internal DSLs

width_=(100.percent());

Tuesday, May 18, 2010

Page 38: Internal DSLs

width_=(int2UnitInt(100).percent());

Tuesday, May 18, 2010

Page 39: Internal DSLs

class UnitIntWrapper(i: Int) { ...

def percent = new PercentUnitInt(i)

class PercentUnitInt(i: Int) extends UnitInt { override def toString = i + "%" }}

Tuesday, May 18, 2010

Page 40: Internal DSLs

ruby

Tuesday, May 18, 2010

Page 41: Internal DSLs

Person.find_by_name('Zef')

Tuesday, May 18, 2010

Page 42: Internal DSLs

class Person def self.find(key, value) puts "You want results from #{key} with a value of #{value}" endend

Person.find('name', 'Zef')

Tuesday, May 18, 2010

Page 43: Internal DSLs

class Person def self.find(key, value) puts "You want results from #{key} with a value of #{value}" end

def self.method_missing(id, *args) if id.id2name =~ /find_by_(.+)/ return self.find(Regexp.last_match(1), args[0]) else raise NoMethodError end endend

Person.find_by_name('Zef')

Tuesday, May 18, 2010

Page 44: Internal DSLs

reflection

Tuesday, May 18, 2010

Page 45: Internal DSLs

public class Person { @Persistent public String name; @Persistent public int age;}

Tuesday, May 18, 2010

Page 46: Internal DSLs

void persist(Object obj) { Class cls = obj.getClass(); Field[] fields = cls.getFields(); for(Field f : fields) { Persistent anno = f.getAnnotation(Persistent.class); if(anno != null) { System.out.println(f.getName()); } }}

Tuesday, May 18, 2010

Page 47: Internal DSLs

macros

Tuesday, May 18, 2010

Page 48: Internal DSLs

int one() { printf("One!"); return 1;}

int two() { printf("Two!"); return 2;}

...

int c = choice(n == 1, one(), two());

Tuesday, May 18, 2010

Page 49: Internal DSLs

int choice(BOOL c, int ifTrue, int ifFalse) { return c ? ifTrue : ifFalse;}

Tuesday, May 18, 2010

Page 50: Internal DSLs

int one() { printf("One!"); return 1;}

int two() { printf("Two!"); return 2;}

...

int c = choice(n == 1, one(), two());

Tuesday, May 18, 2010

Page 51: Internal DSLs

#define CHOICE(c,ifTrue,ifFalse) \ (c) ? (ifTrue) : (ifFalse)

Tuesday, May 18, 2010

Page 52: Internal DSLs

#define CHOICE(c,ifTrue,ifFalse) \ (c) ? (ifTrue) : (ifFalse)

int c = CHOICE(n == 1, one(), two());

Tuesday, May 18, 2010

Page 53: Internal DSLs

#define CHOICE(c,ifTrue,ifFalse) \ (c) ? (ifTrue) : (ifFalse)

int c = CHOICE(n == 1, one(), two());

int c = (n == 1) ? (one()) : (two());

Tuesday, May 18, 2010

Page 54: Internal DSLs

clojure (a Lisp)

Tuesday, May 18, 2010

Page 55: Internal DSLs

homoiconiccode is datadata is code

Tuesday, May 18, 2010

Page 56: Internal DSLs

(+ 1 2 3)

Tuesday, May 18, 2010

Page 57: Internal DSLs

(+ 1 2 3) = 6

Tuesday, May 18, 2010

Page 58: Internal DSLs

'(+ 1 2 3)

Tuesday, May 18, 2010

Page 59: Internal DSLs

(first '(+ 1 2 3))

Tuesday, May 18, 2010

Page 60: Internal DSLs

(first '(+ 1 2 3)) = +

Tuesday, May 18, 2010

Page 61: Internal DSLs

(cons '+ (reverse (rest '(+ 1 2 3))))

Tuesday, May 18, 2010

Page 62: Internal DSLs

(cons '+ (reverse (rest '(+ 1 2 3))))

= (+ 3 2 1)

Tuesday, May 18, 2010

Page 63: Internal DSLs

(cons '+ (reverse (rest '(+ 1 2 3))))(eval

)

Tuesday, May 18, 2010

Page 64: Internal DSLs

(cons '+ (reverse (rest '(+ 1 2 3))))

= 6

(eval)

Tuesday, May 18, 2010

Page 65: Internal DSLs

(if (= n 10) (print "It was ten!”) nil)

Tuesday, May 18, 2010

Page 66: Internal DSLs

(when (= n 10) (print "It was ten!”))

Tuesday, May 18, 2010

Page 67: Internal DSLs

(defn when [c ifTrue] (if c ifTrue nil))

Tuesday, May 18, 2010

Page 68: Internal DSLs

(when (= n 10) (print "It was ten!”))

Tuesday, May 18, 2010

Page 69: Internal DSLs

(defmacro when [c ifTrue] (list 'if c ifTrue 'nil))

Tuesday, May 18, 2010

Page 70: Internal DSLs

(defmacro when [c ifTrue] `(if ~c ~ifTrue nil))

Tuesday, May 18, 2010

Page 71: Internal DSLs

(when (= n 10) (print "It was ten!”))

(if (= n 10) (print "It was ten!”) nil)

Tuesday, May 18, 2010

Page 72: Internal DSLs

(when (= n 10) (print "It was ten!”))

(if (= n 10) (print "It was ten!”) nil)

Tuesday, May 18, 2010

Page 73: Internal DSLs

(loop for i in *random* counting (evenp i) into evens counting (oddp i) into odds summing i into total maximizing i into max minimizing i into min finally (return (list min max total evens odds)))

Tuesday, May 18, 2010

Page 74: Internal DSLs

(defent User [:username :string {:unique true}] [:openid :string] [:email :email] [:points :int])

http://github.com/zefhemel/adia

Tuesday, May 18, 2010

Page 75: Internal DSLs

(defwebfn show [u User] [:h1 "Posted items"] [:ul (for [i (query Item :where {:author (:_id u)} :order-by {:date -1})] [:li (:title i)])])

Tuesday, May 18, 2010

Page 76: Internal DSLs

advantages

Tuesday, May 18, 2010

Page 77: Internal DSLs

easy to develop

builds on existing platform

existing community

Tuesday, May 18, 2010

Page 78: Internal DSLs

disadvantages

Tuesday, May 18, 2010

Page 79: Internal DSLs

<h1>Hello, Rails!</h1>

<%= link_to "My Blog", post_path %>

http://zef.me/2308/when-rails-fails

Tuesday, May 18, 2010

Page 80: Internal DSLs

<h1>Hello, Rails!</h1>

<%= link_to "My Blog", post_path %>

post_url failed to generate from {:controller=>"posts", :action=>"show"} – you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: ["posts", :id] – are they all satisfied?

http://zef.me/2308/when-rails-fails

Tuesday, May 18, 2010

Page 81: Internal DSLs

<%= link_to 'Destroy', post, :confrm => 'Are you sure?', :method => :delete %> 

Tuesday, May 18, 2010

Page 82: Internal DSLs

<%= link_to 'Destroy', post, :confrm => 'Are you sure?', :method => :delete %> 

Tuesday, May 18, 2010

Page 83: Internal DSLs

class Post < ActiveRecord::Base  validates_presence_of :namend

Tuesday, May 18, 2010

Page 84: Internal DSLs

class Post < ActiveRecord::Base  validates_presence_of :namend

Tuesday, May 18, 2010

Page 85: Internal DSLs

no static checking

late discovery of errors

Tuesday, May 18, 2010

Page 86: Internal DSLs

no static checking

late discovery of errors

Tuesday, May 18, 2010

Page 87: Internal DSLs

Tuesday, May 18, 2010

Page 88: Internal DSLs

Tuesday, May 18, 2010

Page 89: Internal DSLs

non-domain specific error messages

Tuesday, May 18, 2010

Page 90: Internal DSLs

Tuesday, May 18, 2010

Page 91: Internal DSLs

errors hard to trace back to origin

Tuesday, May 18, 2010

Page 92: Internal DSLs

Table t = table("table").as("t");Table t1 = table("table1").as("t1");Field tId = t.field("id");Field t1Id = t1.field("id");Field t1Time = t1.field("time");

Sql sql = select(tId).from(t).join(inner(t1, tId.eq(t1Id)))  .where(and(tId.eq("'a'"), t1Time.between("'1900'", "'2000'")))   .groupBy(tId).having(tId.gt("1"))   .orderBy(asc(tId));

http://www.jequel.de

Tuesday, May 18, 2010

Page 93: Internal DSLs

limited freedom in syntax

Tuesday, May 18, 2010

Page 94: Internal DSLs

+ -quick development lack of static checking

built on existing platform

errors hard to trace back to origin

existing community bad error messages

limited freedom in syntax

Tuesday, May 18, 2010

Page 95: Internal DSLs

?Tuesday, May 18, 2010