Internal DSLs

2,611 views

Published on

My lecture about internal DSL implementation techniques.

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,611
On SlideShare
0
From Embeds
0
Number of Embeds
591
Actions
Shares
0
Downloads
38
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide

Internal DSLs

  1. 1. internal DSLs Zef Hemel Tuesday, May 18, 2010
  2. 2. 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
  3. 3. Internal DSLs are particular ways of abusing a host language to give the host language the feel of a particular language. Martin Fowler Tuesday, May 18, 2010
  4. 4. fluent interfaces flexible syntax method missing reflection macros Tuesday, May 18, 2010
  5. 5. Tuesday, May 18, 2010
  6. 6. fluent interfaces Tuesday, May 18, 2010
  7. 7. 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
  8. 8. customer.newOrder() .with(1, "Billy") .with(2, "Janso") .with(4, "Traby") .priorityRush() .done(); Tuesday, May 18, 2010
  9. 9. public class Customer { ... public OrderBuilder newOrder() { return new OrderBuilder(this); } } Tuesday, May 18, 2010
  10. 10. 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
  11. 11. 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
  12. 12. flexible syntax Tuesday, May 18, 2010
  13. 13. 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
  14. 14. in Scala Tuesday, May 18, 2010
  15. 15. 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
  16. 16. 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
  17. 17. 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
  18. 18. 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
  19. 19. 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
  20. 20. a >> b == a.>>(b) Tuesday, May 18, 2010
  21. 21. section { header("All entries") ... } Tuesday, May 18, 2010
  22. 22. section(() => { header("All entries") ... }) Tuesday, May 18, 2010
  23. 23. section(() => { header("All entries") ... }) def section(content : => Unit) { write("<div class='section'>") content write("</div>") } Tuesday, May 18, 2010
  24. 24. ruby Tuesday, May 18, 2010
  25. 25. create_table :posts do |t| t.string :name t.string :title t.text :content end Tuesday, May 18, 2010
  26. 26. create_table(:posts,do |t| t.string(:name) t.string(:title) t.text (:content) end) Tuesday, May 18, 2010
  27. 27. class Post < ActiveRecord::Base validates_presence_of :name, :title validates_length_of :title, :minimum => 5 end Tuesday, May 18, 2010
  28. 28. class Post < ActiveRecord::Base validates_presence_of(:name, :title) validates_length_of(:title, :minimum => 5) end Tuesday, May 18, 2010
  29. 29. method missing Tuesday, May 18, 2010
  30. 30. scala Tuesday, May 18, 2010
  31. 31. width = 100 percent; Tuesday, May 18, 2010
  32. 32. width = 100 percent; def width_=(w: UnitInt) { ... } Tuesday, May 18, 2010
  33. 33. width_=(100 percent); Tuesday, May 18, 2010
  34. 34. width_=(100.percent); Tuesday, May 18, 2010
  35. 35. width_=(100.percent()); Tuesday, May 18, 2010
  36. 36. implicit def int2UnitInt(i: Int) = new UnitIntWrapper(i) Tuesday, May 18, 2010
  37. 37. width_=(100.percent()); Tuesday, May 18, 2010
  38. 38. width_=(int2UnitInt(100).percent()); Tuesday, May 18, 2010
  39. 39. class UnitIntWrapper(i: Int) { ... def percent = new PercentUnitInt(i) class PercentUnitInt(i: Int) extends UnitInt { override def toString = i + "%" } } Tuesday, May 18, 2010
  40. 40. ruby Tuesday, May 18, 2010
  41. 41. Person.find_by_name('Zef') Tuesday, May 18, 2010
  42. 42. class Person def self.find(key, value) puts "You want results from #{key} with a value of #{value}" end end Person.find('name', 'Zef') Tuesday, May 18, 2010
  43. 43. 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 end end Person.find_by_name('Zef') Tuesday, May 18, 2010
  44. 44. reflection Tuesday, May 18, 2010
  45. 45. public class Person { @Persistent public String name; @Persistent public int age; } Tuesday, May 18, 2010
  46. 46. 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
  47. 47. macros Tuesday, May 18, 2010
  48. 48. int one() { printf("One!"); return 1; } int two() { printf("Two!"); return 2; } ... int c = choice(n == 1, one(), two()); Tuesday, May 18, 2010
  49. 49. int choice(BOOL c, int ifTrue, int ifFalse) { return c ? ifTrue : ifFalse; } Tuesday, May 18, 2010
  50. 50. int one() { printf("One!"); return 1; } int two() { printf("Two!"); return 2; } ... int c = choice(n == 1, one(), two()); Tuesday, May 18, 2010
  51. 51. #define CHOICE(c,ifTrue,ifFalse) (c) ? (ifTrue) : (ifFalse) Tuesday, May 18, 2010
  52. 52. #define CHOICE(c,ifTrue,ifFalse) (c) ? (ifTrue) : (ifFalse) int c = CHOICE(n == 1, one(), two()); Tuesday, May 18, 2010
  53. 53. #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
  54. 54. clojure (a Lisp) Tuesday, May 18, 2010
  55. 55. homoiconic code is data data is code Tuesday, May 18, 2010
  56. 56. (+ 1 2 3) Tuesday, May 18, 2010
  57. 57. (+ 1 2 3) =6 Tuesday, May 18, 2010
  58. 58. '(+ 1 2 3) Tuesday, May 18, 2010
  59. 59. (first '(+ 1 2 3)) Tuesday, May 18, 2010
  60. 60. (first '(+ 1 2 3)) =+ Tuesday, May 18, 2010
  61. 61. (cons '+ (reverse (rest '(+ 1 2 3)))) Tuesday, May 18, 2010
  62. 62. (cons '+ (reverse (rest '(+ 1 2 3)))) = (+ 3 2 1) Tuesday, May 18, 2010
  63. 63. (eval (cons '+ (reverse (rest '(+ 1 2 3))))) Tuesday, May 18, 2010
  64. 64. (eval (cons '+ (reverse (rest '(+ 1 2 3))))) =6 Tuesday, May 18, 2010
  65. 65. (if (= n 10) (print "It was ten!”) nil) Tuesday, May 18, 2010
  66. 66. (when (= n 10) (print "It was ten!”)) Tuesday, May 18, 2010
  67. 67. (defn when [c ifTrue] (if c ifTrue nil)) Tuesday, May 18, 2010
  68. 68. (when (= n 10) (print "It was ten!”)) Tuesday, May 18, 2010
  69. 69. (defmacro when [c ifTrue] (list 'if c ifTrue 'nil)) Tuesday, May 18, 2010
  70. 70. (defmacro when [c ifTrue] `(if ~c ~ifTrue nil)) Tuesday, May 18, 2010
  71. 71. (when (= n 10) (print "It was ten!”)) (if (= n 10) (print "It was ten!”) nil) Tuesday, May 18, 2010
  72. 72. (when (= n 10) (print "It was ten!”)) (if (= n 10) (print "It was ten!”) nil) Tuesday, May 18, 2010
  73. 73. (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
  74. 74. (defent User [:username :string {:unique true}] [:openid :string] [:email :email] [:points :int]) http://github.com/zefhemel/adia Tuesday, May 18, 2010
  75. 75. (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
  76. 76. advantages Tuesday, May 18, 2010
  77. 77. easy to develop builds on existing platform existing community Tuesday, May 18, 2010
  78. 78. disadvantages Tuesday, May 18, 2010
  79. 79. <h1>Hello, Rails!</h1> <%= link_to "My Blog", post_path %> http://zef.me/2308/when-rails-fails Tuesday, May 18, 2010
  80. 80. <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
  81. 81. <%= link_to 'Destroy', post, :confrm => 'Are you sure?', :method => :delete %>  Tuesday, May 18, 2010
  82. 82. <%= link_to 'Destroy', post, :confrm => 'Are you sure?', :method => :delete %>  Tuesday, May 18, 2010
  83. 83. class Post < ActiveRecord::Base   validates_presence_of :nam end Tuesday, May 18, 2010
  84. 84. class Post < ActiveRecord::Base   validates_presence_of :nam end Tuesday, May 18, 2010
  85. 85. no static checking late discovery of errors Tuesday, May 18, 2010
  86. 86. no static checking late discovery of errors Tuesday, May 18, 2010
  87. 87. Tuesday, May 18, 2010
  88. 88. Tuesday, May 18, 2010
  89. 89. non-domain specific error messages Tuesday, May 18, 2010
  90. 90. Tuesday, May 18, 2010
  91. 91. errors hard to trace back to origin Tuesday, May 18, 2010
  92. 92. 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
  93. 93. limited freedom in syntax Tuesday, May 18, 2010
  94. 94. + - quick development lack of static checking built on existing errors hard to trace platform back to origin existing community bad error messages limited freedom in syntax Tuesday, May 18, 2010
  95. 95. Tuesday, May 18, 2010 ?

×