internal dsls

Post on 10-May-2015

2.212 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

My lecture about internal DSL implementation techniques.

TRANSCRIPT

internal DSLsZef Hemel

Tuesday, May 18, 2010

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

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

fluent interfaces

flexible syntax

reflection

macros

method missing

Tuesday, May 18, 2010

Tuesday, May 18, 2010

fluent interfaces

Tuesday, May 18, 2010

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

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

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

flexible syntax

Tuesday, May 18, 2010

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

in Scala

Tuesday, May 18, 2010

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

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

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

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

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

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

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

Tuesday, May 18, 2010

ruby

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

method missing

Tuesday, May 18, 2010

scala

Tuesday, May 18, 2010

width = 100 percent;

Tuesday, May 18, 2010

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

width = 100 percent;

Tuesday, May 18, 2010

width_=(100 percent);

Tuesday, May 18, 2010

width_=(100.percent);

Tuesday, May 18, 2010

width_=(100.percent());

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

width_=(100.percent());

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

class UnitIntWrapper(i: Int) { ...

def percent = new PercentUnitInt(i)

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

Tuesday, May 18, 2010

ruby

Tuesday, May 18, 2010

Person.find_by_name('Zef')

Tuesday, May 18, 2010

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

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

reflection

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

macros

Tuesday, May 18, 2010

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

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

...

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

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

...

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

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

Tuesday, May 18, 2010

#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

clojure (a Lisp)

Tuesday, May 18, 2010

homoiconiccode is datadata is code

Tuesday, May 18, 2010

(+ 1 2 3)

Tuesday, May 18, 2010

(+ 1 2 3) = 6

Tuesday, May 18, 2010

'(+ 1 2 3)

Tuesday, May 18, 2010

(first '(+ 1 2 3))

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

= (+ 3 2 1)

Tuesday, May 18, 2010

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

)

Tuesday, May 18, 2010

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

= 6

(eval)

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

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

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

Tuesday, May 18, 2010

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

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

Tuesday, May 18, 2010

(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

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

http://github.com/zefhemel/adia

Tuesday, May 18, 2010

(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

advantages

Tuesday, May 18, 2010

easy to develop

builds on existing platform

existing community

Tuesday, May 18, 2010

disadvantages

Tuesday, May 18, 2010

<h1>Hello, Rails!</h1>

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

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

Tuesday, May 18, 2010

<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

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

Tuesday, May 18, 2010

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

Tuesday, May 18, 2010

class Post < ActiveRecord::Base  validates_presence_of :namend

Tuesday, May 18, 2010

class Post < ActiveRecord::Base  validates_presence_of :namend

Tuesday, May 18, 2010

no static checking

late discovery of errors

Tuesday, May 18, 2010

no static checking

late discovery of errors

Tuesday, May 18, 2010

Tuesday, May 18, 2010

Tuesday, May 18, 2010

non-domain specific error messages

Tuesday, May 18, 2010

Tuesday, May 18, 2010

errors hard to trace back to origin

Tuesday, May 18, 2010

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

limited freedom in syntax

Tuesday, May 18, 2010

+ -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

?Tuesday, May 18, 2010

top related