Slide 1

Slide 1 text

HELLO, DECLARATIVE WORLD http://codon.com/hello-declarative-world

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

UNSTRUCTURED PROCEDURAL FUNCTIONAL DECLARATIVE IMPERATIVE MORE ABSTRACT

Slide 5

Slide 5 text

PROGRAMMER (A HUMAN) SOLVER (A MACHINE) HOW WHAT

Slide 6

Slide 6 text

UNSTRUCTURED PROCEDURAL FUNCTIONAL RELATIONAL DECLARATIVE IMPERATIVE MORE ABSTRACT

Slide 7

Slide 7 text

“FUNCTION” 19 23 42 x y x + y

Slide 8

Slide 8 text

“RELATION” y is 5 x = y x is 5 y is ?

Slide 9

Slide 9 text

“RELATION” x is 4 x < y y is 5 x is ? x is 3 x is 2 x is 1 x is 0

Slide 10

Slide 10 text

LET’S BUILD OUR OWN!

Slide 11

Slide 11 text

GOAL STATE STATE STATE STATE ⋮

Slide 12

Slide 12 text

VARIABLES

Slide 13

Slide 13 text

>> x, y = Variable.new(:x), Variable.new(:y) => [x, y] >> x == x => true >> x == y => false >> x == Variable.new(:x) => false

Slide 14

Slide 14 text

class Variable def initialize(name) @name = name end def inspect @name end end

Slide 15

Slide 15 text

STATES

Slide 16

Slide 16 text

class State def initialize(variables = [], values = {}) @variables, @values = variables, values end attr_reader :variables, :values end

Slide 17

Slide 17 text

>> state = State.new => # >> state, (x, y, z) = state.create_variables [:x, :y, :z] => [#, [x, y, z]] >> state.variables => [x, y, z] >> state = state.assign_values x => y, y => z, z => 5 => #y, y=>z, z=>5}> >> state.values => {x=>y, y=>z, z=>5}

Slide 18

Slide 18 text

class State def create_variables(names) new_variables = names.map { |name| Variable.new(name) } [ State.new(variables + new_variables, values), new_variables ] end def assign_values(new_values) State.new(variables, values.merge(new_values)) end end

Slide 19

Slide 19 text

>> state.values => {x=>y, y=>z, z=>5} >> state.value_of x => 5 >> state.value_of 7 => 7 >> state, (a, b, c) = state.create_variables [:a, :b, :c] => [#, [a, b, c]] >> state.value_of a => a

Slide 20

Slide 20 text

class State def value_of(key) if values.has_key?(key) value_of values.fetch(key) else key end end end

Slide 21

Slide 21 text

UNIFICATION

Slide 22

Slide 22 text

>> state, (x, y) = State.new.create_variables [:x, :y] => [#, [x, y]] >> state = state.unify(x, x) => # >> state.values => {}

Slide 23

Slide 23 text

>> state = state.unify(x, y) => #y}> >> state.values => {x=>y} >> state = state.unify(x, 5) => #y, y=>5}> >> state.values => {x=>y, y=>5} >> state.value_of x => 5 >> state.unify(y, 6) => nil

Slide 24

Slide 24 text

class State def unify(a, b) a, b = value_of(a), value_of(b) if a == b self elsif a.is_a?(Variable) assign_values a => b elsif b.is_a?(Variable) assign_values b => a end end end

Slide 25

Slide 25 text

GOALS

Slide 26

Slide 26 text

class Goal def initialize(&block) @block = block end def pursue_in(state) @block.call state end end

Slide 27

Slide 27 text

BASIC GOALS

Slide 28

Slide 28 text

EQUAL VALUE VALUE

Slide 29

Slide 29 text

>> state, (x, y, z) = State.new.create_variables [:x, :y, :z] => [#, [x, y, z]] >> goal = Goal.equal(x, 5) => #> >> states = goal.pursue_in(state) => #:each> >> states.next => #5}> >> _.values => {x=>5} >> states.next StopIteration: iteration reached an end

Slide 30

Slide 30 text

def Goal.equal(a, b) Goal.new do |state| state = state.unify(a, b) Enumerator.new do |yielder| yielder.yield state if state end end end

Slide 31

Slide 31 text

WITH VARIABLES GOAL X Y …

Slide 32

Slide 32 text

>> goal = Goal.with_variables { |x| Goal.equal(x, 5) } => #> >> states = goal.pursue_in(State.new) => #:each> >> state = states.first => #5}> >> state.values => {x=>5}

Slide 33

Slide 33 text

def Goal.with_variables(&block) names = block.parameters. map { |type, name| name } Goal.new do |state| state, variables = state.create_variables(names) goal = block.call(*variables) goal.pursue_in state end end

Slide 34

Slide 34 text

COMBINING GOALS

Slide 35

Slide 35 text

EITHER GOAL GOAL

Slide 36

Slide 36 text

>> goal = Goal.with_variables { |x| Goal.either(Goal.equal(x, 5), Goal.equal(x, 6)) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.values => {x=>5} >> states.next.values => {x=>6}

Slide 37

Slide 37 text

def Goal.either(first_goal, second_goal) Goal.new do |state| first_stream = first_goal.pursue_in(state) second_stream = second_goal.pursue_in(state) first_stream.interleave_with(second_stream) end end

Slide 38

Slide 38 text

>> letters = 'a'.upto('z') => # >> numbers = 1.upto(10) => # >> letters.interleave_with(numbers).entries => ["a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i", 9, "j", 10, "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

Slide 39

Slide 39 text

>> letters = ['a', 'b', 'c'].cycle => # >> numbers = 1.upto(Float::INFINITY) => # >> letters.interleave_with(numbers).take(50) => ["a", 1, "b", 2, "c", 3, "a", 4, "b", 5, "c", 6, "a", 7, "b", 8, "c", 9, "a", 10, "b", 11, "c", 12, "a", 13, "b", 14, "c", 15, "a", 16, "b", 17, "c", 18, "a", 19, "b", 20, "c", 21, "a", 22, "b", 23, "c", 24, "a", 25]

Slide 40

Slide 40 text

class Enumerator def interleave_with(other) enumerators = self, other Enumerator.new do |yielder| until enumerators.empty? loop do enumerator = enumerators.shift yielder.yield enumerator.next enumerators.push enumerator end end end end end

Slide 41

Slide 41 text

BOTH GOAL GOAL

Slide 42

Slide 42 text

>> goal = Goal.with_variables { |x, y| Goal.both( Goal.equal(x, 5), Goal.equal(y, 7) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.values => {x=>5, y=>7}

Slide 43

Slide 43 text

>> goal = Goal.with_variables { |a, b| Goal.both( Goal.equal(a, 7), Goal.either( Goal.equal(b, 5), Goal.equal(b, 6) ) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.values => {a=>7, b=>5} >> states.next.values => {a=>7, b=>6}

Slide 44

Slide 44 text

>> goal = Goal.with_variables { |x| Goal.both( Goal.equal(1, x), Goal.equal(x, 2) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.values StopIteration: iteration reached an end

Slide 45

Slide 45 text

def Goal.both(first_goal, second_goal) Goal.new do |state| states = first_goal.pursue_in(state) second_goal.pursue_in_each(states) end end

Slide 46

Slide 46 text

class Goal def pursue_in_each(states) Enumerator.new do |yielder| results = pursue_in(states.next) results = results.interleave_with(pursue_in_each(states)) results.each do |state| yielder.yield state end end end end

Slide 47

Slide 47 text

WITH VARIABLES G X Y … EQUAL V V STATE VARIABLE EITHER G G BOTH G G

Slide 48

Slide 48 text

µKANREN

Slide 49

Slide 49 text

WHAT CAN IT DO?

Slide 50

Slide 50 text

>> pair = Pair.new(5, 9) => (5, 9) >> pair.left => 5 >> pair.right => 9

Slide 51

Slide 51 text

Pair = Struct.new(:left, :right) do def inspect "(#{left.inspect}, #{right.inspect})" end end

Slide 52

Slide 52 text

>> goal = Goal.with_variables { |x, y| Goal.equal( Pair.new(3, x), Pair.new(y, Pair.new(5, y)) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> state = states.first => #3, x=>(5, 3)}> >> state.values => {y=>3, x=>(5, 3)}

Slide 53

Slide 53 text

class State def unify(a, b) a, b = value_of(a), value_of(b) if a == b self elsif a.is_a?(Variable) assign_values a => b elsif b.is_a?(Variable) assign_values b => a end end end

Slide 54

Slide 54 text

end end end elsif a.is_a?(Pair) && b.is_a?(Pair) state = unify(a.left, b.left) state.unify(a.right, b.right) if state class State def unify(a, b) a, b = value_of(a), value_of(b) if a == b self elsif a.is_a?(Variable) assign_values a => b elsif b.is_a?(Variable) assign_values b => a

Slide 55

Slide 55 text

else key end end end class State def value_of(key) if values.has_key?(key) value_of values.fetch(key)

Slide 56

Slide 56 text

else key end end end class State def value_of(key) if values.has_key?(key) value_of values.fetch(key) elsif key.is_a?(Pair) Pair.new( value_of(key.left), value_of(key.right) )

Slide 57

Slide 57 text

class State def results(n) variables.first(n). map { |variable| value_of(variable) } end def result results(1).first end end

Slide 58

Slide 58 text

>> state.values => {y=>3, x=>(5, 3)} >> state.variables => [x, y] >> state.results 2 => [(5, 3), 3] >> state.result => (5, 3)

Slide 59

Slide 59 text

LISTS

Slide 60

Slide 60 text

['a', 'b', 'c'] ('a', ('b', ('c', •)))

Slide 61

Slide 61 text

EMPTY_LIST = :empty def to_list(array) if array.empty? EMPTY_LIST else first, *rest = array Pair.new(first, to_list(rest)) end end

Slide 62

Slide 62 text

>> to_list ['a', 'b', 'c'] => ('a', ('b', ('c', :empty)))

Slide 63

Slide 63 text

def from_list(list) if list == EMPTY_LIST [] else first, rest = list.left, list.right [first, *from_list(rest)] end end

Slide 64

Slide 64 text

>> from_list \ Pair.new('a', Pair.new('b', Pair.new('c', EMPTY_LIST) ) ) => ['a', 'b', 'c']

Slide 65

Slide 65 text

>> goal = Goal.with_variables { |x, y, z| Goal.equal( to_list([x, 2, z]), to_list([1, y, 3]) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.values => {x=>1, y=>2, z=>3}

Slide 66

Slide 66 text

def append(a, b, c) Goal.either( Goal.both( Goal.equal(a, EMPTY_LIST), Goal.equal(b, c) ), Goal.with_variables { |first, rest_of_a, rest_of_c| Goal.both( Goal.both( Goal.equal(a, Pair.new(first, rest_of_a)), Goal.equal(c, Pair.new(first, rest_of_c)) ), append(rest_of_a, b, rest_of_c) ) } ) end

Slide 67

Slide 67 text

>> goal = Goal.with_variables { |x| append( to_list(['h', 'e']), to_list(['l', 'l', 'o']), x ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> state.next.result => ("h", ("e", ("l", ("l", ("o", :empty))))) >> from_list _ => ["h", "e", "l", "l", "o"]

Slide 68

Slide 68 text

>> goal = Goal.with_variables { |x| append( x, to_list(['l', 'o']), to_list(['h', 'e', 'l', 'l', 'o']) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> from_list states.next.result => ["h", "e", "l"]

Slide 69

Slide 69 text

>> goal = Goal.with_variables { |x, y| append( x, y, to_list(['h', 'e', 'l', 'l', 'o']) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.results(2) => [:empty, ("h", ("e", ("l", ("l", ("o", :empty)))))] >> _.map { |list| from_list(list) } => [[], ["h", "e", "l", "l", "o"]]

Slide 70

Slide 70 text

>> states.next.results(2). map { |list| from_list(list) } => [["h", "e"], ["l", "l", "o"]] >> states.next.results(2). map { |list| from_list(list) } => [["h"], ["e", "l", "l", "o"]] >> states = goal.pursue_in(State.new) => #:each> >> states.next.results(2) => [:empty, ("h", ("e", ("l", ("l", ("o", :empty)))))] >> _.map { |list| from_list(list) } => [[], ["h", "e", "l", "l", "o"]]

Slide 71

Slide 71 text

>> states = goal.pursue_in(State.new) => #:each> >> states.each do |state| p state.results(2).map { |list| from_list(list) } end [[], ["h", "e", "l", "l", "o"]] [["h"], ["e", "l", "l", "o"]] [["h", "e"], ["l", "l", "o"]] [["h", "e", "l"], ["l", "o"]] [["h", "e", "l", "l"], ["o"]] [["h", "e", "l", "l", "o"], []] => nil

Slide 72

Slide 72 text

NUMBERS

Slide 73

Slide 73 text

3 (+, (+, (+, •)))

Slide 74

Slide 74 text

INC, ZERO = :+, :z def to_peano(number) if number.zero? ZERO else Pair.new(INC, to_peano(number - 1)) end end

Slide 75

Slide 75 text

>> to_peano 3 => (:+, (:+, (:+, :z)))

Slide 76

Slide 76 text

def from_peano(peano) if peano == ZERO 0 else from_peano(peano.right) + 1 end end

Slide 77

Slide 77 text

>> from_peano \ Pair.new(INC, Pair.new(INC, Pair.new(INC, ZERO) ) ) => 3

Slide 78

Slide 78 text

def add(x, y, z) Goal.either( Goal.both( Goal.equal(x, ZERO), Goal.equal(y, z) ), Goal.with_variables { |smaller_x, smaller_z| Goal.both( Goal.both( Goal.equal(x, Pair.new(INC, smaller_x)), Goal.equal(z, Pair.new(INC, smaller_z)) ), add(smaller_x, y, smaller_z) ) } ) end

Slide 79

Slide 79 text

>> goal = Goal.with_variables { |x| add( to_peano(5), to_peano(3), x ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.result => (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, :z)))))))) >> from_peano _ => 8

Slide 80

Slide 80 text

>> goal = Goal.with_variables { |x| add( x, to_peano(3), to_peano(8) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> from_peano states.next.result => 5

Slide 81

Slide 81 text

>> goal = Goal.with_variables { |x, y| add( x, y, to_peano(8) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.each do |state| p state.results(2).map { |peano| from_peano(peano) } end [0, 8]

Slide 82

Slide 82 text

>> states = goal.pursue_in(State.new) => #:each> >> states.each do |state| p state.results(2).map { |peano| from_peano(peano) } end [0, 8] [1, 7] [2, 6] [3, 5] [4, 4] [5, 3] [6, 2] [7, 1] [8, 0] => nil

Slide 83

Slide 83 text

def multiply(x, y, z) Goal.either( Goal.both( Goal.equal(x, ZERO), Goal.equal(z, ZERO) ), Goal.with_variables { |smaller_x, smaller_z| Goal.both( Goal.both( Goal.equal(x, Pair.new(INC, smaller_x)), add(smaller_z, y, z) ), multiply(smaller_x, y, smaller_z) ) } ) end

Slide 84

Slide 84 text

>> goal = Goal.with_variables { |x| multiply( to_peano(3), to_peano(8), x ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.next.result => (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, (:+, :z)))))))))))))))))))))))) >> from_peano _ => 24

Slide 85

Slide 85 text

>> goal = Goal.with_variables { |x, y| multiply( x, y, to_peano(24) ) } => #> >> states = goal.pursue_in(State.new) => #:each> >> states.take(8).each do |state| p state.results(2).map { |peano| from_peano(peano) } end [1, 24] [2, 12] [3, 8]

Slide 86

Slide 86 text

>> states = goal.pursue_in(State.new) => #:each> >> states.take(8).each do |state| p state.results(2).map { |peano| from_peano(peano) } end [1, 24] [2, 12] [3, 8] [4, 6] [6, 4] [8, 3] [12, 2] [24, 1] => nil

Slide 87

Slide 87 text

BE DECLARATIVE

Slide 88

Slide 88 text

PROGRAMMER (A HUMAN) SOLVER (A MACHINE) HOW WHAT

Slide 89

Slide 89 text

THANKS. @tomstuart http://codon.com/hello-declarative-world