Denis Defreyne
July 05, 2018
120

# Code as data

Given at RUG::B’s July 2018 edition (http://www.rug-b.de/topics/code-as-data-606).

July 05, 2018

## Transcript

1. CODE AS DATA
DENIS DEFREYNE / RUG-.B / 2018-07-05

2. \$\$\$

3. \$\$\$

4. def total_price
200 + volume * distance
end

5. def total_price
200 + volume * distance
end
sum

6. def total_price
200 + volume * distance
end
sum
product

7. def total_price
200 + volume * distance
end
sum
product
variable

8. def total_price
200 + volume * distance
end
sum
product
constant
variable

9. class Sum
def initialize(left, right)
@left = left
@right = right
end
end

10. class Sum
def initialize(left, right)
@left = left
@right = right
end
end
class Product
def initialize(left, right)
@left = left
@right = right
end
end

11. class Sum
def initialize(left, right)
@left = left
@right = right
end
end
class Product
def initialize(left, right)
@left = left
@right = right
end
end
class Constant
def initialize(value)
@value = value
end
end

12. class Sum
def initialize(left, right)
@left = left
@right = right
end
end
class Product
def initialize(left, right)
@left = left
@right = right
end
end
class Constant
def initialize(value)
@value = value
end
end
class Variable
def initialize(name)
@name = name
end
end

13. def price_formula
Sum.new(
Constant.new(200),
Product.new(
Variable.new(Fvolume),
Variable.new(Fdistance),
)
)
end

14. input = {
volume: 20,
distance: 200,
}
price_formula.evaluate(input)

15. class Variable
def initialize(name)
@name = name
end
def evaluate(input)
input.fetch(@name)
end
end

16. class Constant
def initialize(value)
@value = value
end
def evaluate(_input)
@value
end
end

17. class Sum
def initialize(left, right)
@left = left
@right = right
end
def evaluate(input)
@left.evaluate(input) + @right.evaluate(input)
end
end

18. class Product
def initialize(left, right)
@left = left
@right = right
end
def evaluate(input)
@left.evaluate(input) * @right.evaluate(input)
end
end

19. def price_formula
Sum.new(
Constant.new(200),
Product.new(
Variable.new(Fvolume),
Variable.new(Fdistance),
),
)
end

20. def price_formula
Sum.new(
Constant.new(200),
Product.new(
Variable.new(Fvolume),
Variable.new(Fdistance),
)
)
end
def price_formula
Constant.new(200) + Variable.new(Fvolume) * Variable.new(Fdistance)
end

21. class AbstractExpr
def +(other)
Sum.new(self, other)
end
def *(other)
Product.new(self, other)
end
end

22. class Sum < AbstractExpr
# …
end
class Product < AbstractExpr
# …
end
class Constant < AbstractExpr
# …
end
class Variable < AbstractExpr
# …
end

23. def price_formula
Constant.new(200) + Variable.new(Fvolume) * Variable.new(Fdistance)
end

24. THE POINT

25. Constant.new(200) + Variable.new(Fvolume) * Variable.new(Fdistance)

26. Constant.new(200) + Variable.new(Fvolume) * Variable.new(Fdistance)

+ UV> 4200
const UV> 200
* UV> 4000
var volume UV> 20
var distance UV> 200

27. class Node
def initialize(name, value, children)
@name = name
@value = value
@children = children
end
end

28. + UV> 4200
const UV> 200
* UV> 4000
var volume UV> 20
var distance UV> 200

29. + UV> 4200
const UV> 200
* UV> 4000
var volume UV> 20
var distance UV> 200
Node.new("+", 4200, [const_node, product_node])

30. + UV> 4200
const UV> 200
* UV> 4000
var volume UV> 20
var distance UV> 200
Node.new("+", 4200, [const_node, product_node])
Node.new("var volume", 20, [])

31. class Variable < AbstractExpr
def initialize(name)
@name = name
end
def evaluate(input)
Node.new(
"var #{@name}",
input.fetch(@name),
[],
)
end
end

32. class Constant
def initialize(value)
@value = value
end
def evaluate(_input)
Node.new(
'const',
@value,
[],
)
end
end

33. class Sum
def initialize(left, right)
@left = left
@right = right
end
def evaluate(input)
left_node = @left.evaluate(input)
right_node = @right.evaluate(input)
Node.new(
'+',
left_node.value + right_node.value,
[left_node, right_node],
)
end
end

34. class Product
def initialize(left, right)
@left = left
@right = right
end
def evaluate(input)
left_node = @left.evaluate(input)
right_node = @right.evaluate(input)
Node.new(
'*',
left_node.value * right_node.value,
[left_node, right_node],
)
end
end

35. class Node
def to_s
"#{@name} UV> #{@value}\n" +
@children.map { |c| indent(c.to_s) }.join('')
end
private
def indent(s)
s.lines.map { |l| ' ' + l }.join('')
end
end

36. puts price_formula.evaluate(input)

37. puts price_formula.evaluate(input)
+ ./> 4200
const ./> 200
* ./> 4000
var volume ./> 20
var distance ./> 200

38. puts price_formula.evaluate(input).value

39. puts price_formula.evaluate(input).value

4200

40. + UV> 4200
const UV> 200
* UV> 4000
var volume UV> 20
var distance UV> 200

41. +
(= 4200)
const
(= 200)
*
(= 4000)
var distance
(= 200)
var volume
(= 20)

42. IDEA: translate to SQL

43. IDEA: compile with LLVM

45. BACKEND

46. BACKEND formula

47. BACKEND
PARSE +
SERIALIZE
formula

48. PARSING:
parse("200 + volume * distance")

49. PARSING:
parse("200 + volume * distance")
D> Sum.new(
Constant.new(200),
Product.new(
Variable.new(Kvolume),
Variable.new(Kdistance),
)
)

50. SERIALIZING:
formula =
Sum.new(
Constant.new(200),
Product.new(
Variable.new(Fvolume),
Variable.new(Fdistance),
)
)
serialize(formula)

51. SERIALIZING:
formula =
Sum.new(
Constant.new(200),
Product.new(
Variable.new(Fvolume),
Variable.new(Fdistance),
)
)
serialize(formula)
D> ["sum", ["constant", 200], ["product",
["var", "volume"], ["var", "distance"]]

52. BACKEND
PARSE +
SERIALIZE
formula

53. BACKEND
PARSE +
SERIALIZE
DESERIALIZE
+ UNPARSE
formula

54. formula

55. formula A (stable)
formula B (testing)

56. formula A (old)
formula B (stable)

57. Is this over-engineering?

58. TAKE-AWAYS

59. * It’s much easier to reason about data