Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Vertical Assignment in Ruby

Kevin Kuchta
November 13, 2021

Vertical Assignment in Ruby

Ruby's long had leftward assignment (x = 3) and recently got rightward assignment (3 => x). The problem here is obvious: we need a vertical assignment operator. In a complete abdication of good taste and common sense, this talk will walk through correcting this heinous oversight. We'll abuse otherwise-respectable metaprogramming tools like Ripper and TracePoint to add a fully functional vequals operator. While this talk is almost 100% bad-ideas-by-volume, you'll learn a few tricks that you can use in your day-to-day work (or at least to terrify your co-workers).

Kevin Kuchta

November 13, 2021
Tweet

More Decks by Kevin Kuchta

Other Decks in Programming

Transcript

  1. @kkuchta Vertical Assignment Operator NameError: unde f ined local variable

    or method `‖' for main:Object Seems like a valid expression... 3 ‖ x
  2. @kkuchta NameError: unde f ined local variable or method `x'

    for main:Object Vequals Operator Object.define_method(:‖) {|*_|} 3 ‖ x
  3. @kkuchta TracePoint tracepoint = TracePoint.new(:raise) do |tp| # Do cool

    thing here end tracepoint.enable raise "zomg" raise "wtf" raise "bbq"
  4. @kkuchta tracepoint = TracePoint.new(:call) do |tp| # Do cool thing

    here end tracepoint.enable TracePoint 123.to_i foo.bar() [1,2,3].last
  5. @kkuchta TracePoint on every line # run this block on

    every subsequent line... tracepoint = TracePoint.new(:line) do |tp| puts "tracing line #{tp.lineno} in file #{tp.path}" end # ...starting now! tracepoint.enable
  6. @kkuchta TracePoint on every line # run this block on

    every subsequent line... tracepoint = TracePoint.new(:line) do |tp| puts "tracing line #{tp.lineno} in file #{tp.path}" end # ...starting now! tracepoint.enable x = 3 # prints "tracing line 6 in file ./tmp.rb" y = x + 1 # prints "tracing line 7 in file ./tmp.rb" z = x / y # prints "tracing line 9 in file ./tmp.rb"
  7. @kkuchta Binding def foo x = 3 return binding() end

    puts x # error: no local variable x
  8. @kkuchta Binding def foo x = 3 return binding() end

    puts x # error: no local variable x foos_binding = foo
  9. @kkuchta Binding def foo x = 3 return binding() end

    puts x # error: no local variable x foos_binding = foo puts foos_binding.local_variable_get(:x) # 3
  10. @kkuchta Binding TracePoint.new(:line) do |tp| line = tp.line if line.include?

    "x" tp.binding.local_variable_set(:x, "🐥") end end.enable puts x
  11. @kkuchta Binding TracePoint.new(:line) do |tp| line = tp.line if line.include?

    "x" tp.binding.local_variable_set(:x, "🐥") end end.enable puts x
  12. @kkuchta TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] if

    line.include? "x" tp.binding.local_variable_set(:x, "🐥") end end.enable puts x TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] if line.include? "x" tp.binding.local_variable_set(:x, "🐥") end end.enable puts x Binding
  13. @kkuchta TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] if

    line.include? "x" tp.binding.local_variable_set(:x, "🐥") end end.enable puts x Binding
  14. @kkuchta TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] if

    line.include? "x" tp.binding.local_variable_set(:x, "🐥") end end.enable puts x Binding
  15. @kkuchta Binding x = 3 my_binding = binding() my_binding.local_variable_set(:x, 4)

    puts x # 4 my_binding.local_variable_set(:y, 5) puts y # error, y is undefined!
  16. @kkuchta Binding line = tp.line line = File.readlines(tp.path)[tp.lineno - 1]

    tp.binding.local_variable_set(:x, "3") my_binding.eval("def x(*args) = 3")
  17. @kkuchta Success! TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1]

    if line.include?("x") tp.binding.eval("def x(*args) = 3") end end.enable puts x # 3 TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] if line.include?("x") tp.binding.eval("def x(*args) = 3") end end.enable puts x # 3
  18. @kkuchta Success! TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1]

    if line.include?("x") tp.binding.eval("def x(*args) = 3") end end.enable puts x # 3
  19. @kkuchta Success! TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1]

    line.split(' ').each do |token| next if token == "puts" tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x puts y puts z TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] line.split(' ').each do |token| next if token == "puts" tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x puts y puts z
  20. @kkuchta Success! TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1]

    line.split(' ').each do |token| next if token == "puts" tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x puts y puts z
  21. @kkuchta Brittle as heck > line = "puts x #

    hi there!" > line.split(' ') ["puts", "x", "#", "hi", "there!"]
  22. @kkuchta Ripper require 'ripper' pp Ripper.sexp(" x = 3 y

    = x + 1 ") [:program, [[:assign, [:var_field, [:@ident, "x", [2, 2]]], [:@int, "3", [2, 6]]], [:assign, [:var_field, [:@ident, "y", [3, 2]]], [:binary, [:var_ref, [:@ident, "x", [3, 6]]], :+, [:@int, "1", [3, 10]]]]]]
  23. @kkuchta Sexp pp Ripper.sexp('def foo;') # nil pp Ripper.sexp('x =

    (') # nil pp Ripper.sexp('class bar') # nil
  24. @kkuchta Sexp "foo = bar + baz()" ["foo", " ",

    "=", " ", "bar", " ", "+", " ", "baz", "(", ")"] [[:assign, [:var_field, [:@ident, "foo", [1, 0]]], [:binary, [:vcall, [:@ident, "bar", [1, 6]]], :+, [:method_add_arg, [:fcall, [:@ident, "baz", [1, 12]]], [:arg_paren, nil]]]]]
  25. @kkuchta Lexing Ripper.lex("foo = bar + baz()") .map{|_,_,token,_| token} =>

    ["foo", " ", "=", " ", "bar", " ", "+", " ", "baz", "(", ")"]
  26. @kkuchta Split by spaces TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno

    - 1] line.split(' ').each do |token| next if token == "puts" tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x
  27. @kkuchta Lexing TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1]

    tokens = Ripper.lex(line) tokens.each do |_, __, token, ___| next if token == "puts" || token == " " tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] tokens = Ripper.lex(line) tokens.each do |_, __, token, ___| next if token == "puts" || token == " " tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] tokens = Ripper.lex(line) tokens.each do |_, __, token, ___| next if token == "puts" || token == " " tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x
  28. @kkuchta Lexing TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1]

    tokens = Ripper.lex(line) tokens.each do |_, __, token, ___| next if token == "puts" || token == " " tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] tokens = Ripper.lex(line) tokens.each do |_, __, token, ___| next if token == "puts" || token == " " tp.binding.eval("def #{token}(*args) = 3") end end.enable puts x
  29. @kkuchta > Ripper.lex("foo = bar + baz()") [ [[1, 0],

    :on_ident, "foo", CMDARG], [[1, 3], :on_sp, " ", CMDARG], [[1, 4], :on_op, "=", BEG], [[1, 5], :on_sp, " ", BEG], [[1, 6], :on_ident, "bar", ARG], [[1, 9], :on_sp, " ", ARG], [[1, 10], :on_op, "+", BEG], [[1, 11], :on_sp, " ", BEG], [[1, 12], :on_ident, "baz", ARG], [[1, 15], :on_lparen, "(", BEG|LABEL], [[1, 16], :on_rparen, ")", ENDFN] ] Lexing into tokens
  30. @kkuchta > Ripper.lex("foo = bar + baz()") [ [[1, 0],

    :on_ident, "foo", CMDARG], [[1, 3], :on_sp, " ", CMDARG], [[1, 4], :on_op, "=", BEG], [[1, 5], :on_sp, " ", BEG], [[1, 6], :on_ident, "bar", ARG], [[1, 9], :on_sp, " ", ARG], [[1, 10], :on_op, "+", BEG], [[1, 11], :on_sp, " ", BEG], [[1, 12], :on_ident, "baz", ARG], [[1, 15], :on_lparen, "(", BEG|LABEL], [[1, 16], :on_rparen, ")", ENDFN] ] Lexing into tokens
  31. @kkuchta > Ripper.lex("foo = bar + baz()") [ [[1, 0],

    :on_ident, "foo", CMDARG], [[1, 3], :on_sp, " ", CMDARG], [[1, 4], :on_op, "=", BEG], [[1, 5], :on_sp, " ", BEG], [[1, 6], :on_ident, "bar", ARG], [[1, 9], :on_sp, " ", ARG], [[1, 10], :on_op, "+", BEG], [[1, 11], :on_sp, " ", BEG], [[1, 12], :on_ident, "baz", ARG], [[1, 15], :on_lparen, "(", BEG|LABEL], [[1, 16], :on_rparen, ")", ENDFN] ] Lexing into tokens
  32. @kkuchta > Ripper.lex("foo = bar + baz()") [ [[1, 0],

    :on_ident, "foo", CMDARG], [[1, 3], :on_sp, " ", CMDARG], [[1, 4], :on_op, "=", BEG], [[1, 5], :on_sp, " ", BEG], [[1, 6], :on_ident, "bar", ARG], [[1, 9], :on_sp, " ", ARG], [[1, 10], :on_op, "+", BEG], [[1, 11], :on_sp, " ", BEG], [[1, 12], :on_ident, "baz", ARG], [[1, 15], :on_lparen, "(", BEG|LABEL], [[1, 16], :on_rparen, ")", ENDFN] ] Lexing into tokens
  33. @kkuchta > Ripper.lex("foo = bar + baz()") [ [[1, 0],

    :on_ident, "foo", CMDARG], [[1, 3], :on_sp, " ", CMDARG], [[1, 4], :on_op, "=", BEG], [[1, 5], :on_sp, " ", BEG], [[1, 6], :on_ident, "bar", ARG], [[1, 9], :on_sp, " ", ARG], [[1, 10], :on_op, "+", BEG], [[1, 11], :on_sp, " ", BEG], [[1, 12], :on_ident, "baz", ARG], [[1, 15], :on_lparen, "(", BEG|LABEL], [[1, 16], :on_rparen, ")", ENDFN] ] Lexing into tokens
  34. @kkuchta > Ripper.lex("foo = bar + baz()") [ [[1, 0],

    :on_ident, "foo", CMDARG], [[1, 3], :on_sp, " ", CMDARG], [[1, 4], :on_op, "=", BEG], [[1, 5], :on_sp, " ", BEG], [[1, 6], :on_ident, "bar", ARG], [[1, 9], :on_sp, " ", ARG], [[1, 10], :on_op, "+", BEG], [[1, 11], :on_sp, " ", BEG], [[1, 12], :on_ident, "baz", ARG], [[1, 15], :on_lparen, "(", BEG|LABEL], [[1, 16], :on_rparen, ")", ENDFN] ] Lexing into tokens
  35. @kkuchta Searching for identi f iers require 'ripper' TracePoint.new(:line) do

    |tp| line = File.readlines(tp.path)[tp.lineno - 1] tokens = Ripper.lex(line) tokens.each do |_, type, token, ___| if type == :on_ident tp.binding.eval("def #{token}(*args) = 3") end end end.enable puts x
  36. @kkuchta Searching for identi f iers require 'ripper' TracePoint.new(:line) do

    |tp| line = File.readlines(tp.path)[tp.lineno - 1] tokens = Ripper.lex(line) tokens.each do |_, type, token, ___| if type == :on_ident next if tp.binding.receiver.respond_to?(token) || Kernel.respond_to?(token) tp.binding.eval("def #{token}(*args) = 3") end end end.enable puts x # 3 require 'ripper' TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] tokens = Ripper.lex(line) tokens.each do |_, type, token, ___| if type == :on_ident next if tp.binding.receiver.respond_to?(token) || Kernel.respond_to?(token) tp.binding.eval("def #{token}(*args) = 3") end end end.enable puts x # 3
  37. @kkuchta Searching for identi f iers require 'ripper' TracePoint.new(:line) do

    |tp| line = File.readlines(tp.path)[tp.lineno - 1] tokens = Ripper.lex(line) tokens.each do |_, type, token, ___| if type == :on_ident next if tp.binding.receiver.respond_to?(token) || Kernel.respond_to?(token) tp.binding.eval("def #{token}(*args) = 3") end end end.enable puts x # 3
  38. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖
  39. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖ 2. Lex the line above for expressions
  40. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖ 2. Lex the line above for expressions
  41. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖ 2. Lex the line above for expressions 3. Stash the column and expression
  42. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖ 2. Lex the line above for expressions 3. Stash the column and expression vequals_to_process = [ {col: 1, expression: "7"} ]
  43. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖ 2. Lex the line above for expressions 3. Stash the column and expression 4. Store the value of the expression vequals_to_process = [ {col: 1, expression: "7"} ]
  44. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖ 2. Lex the line above for expressions 3. Stash the column and expression 4. Store the value of the expression vequals_to_process = [ {col: 1, value: eval("7")} ]
  45. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1. Lex this line and look for ‖ 2. Lex the line above for expressions 3. Stash the column and expression 4. Store the value of the expression vequals_to_process = [ {col: 1, value: 7} ]
  46. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line vequals_to_process = [ {col: 1, value: 7} ]
  47. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line vequals_to_process = [ {col: 1, value: 7} ]
  48. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1.Lex this line for identi f iers vequals_to_process = [ {col: 1, value: 7} ]
  49. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1.Lex this line for identi f iers Identi f ier 'foo' spans from 0 to 2 vequals_to_process = [ {col: 1, value: 7} ]
  50. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1.Lex this line for identi f iers 2.Is this identi f ier is below a ‖? Identi f ier 'foo' spans from 0 to 2 vequals_to_process = [ {col: 1, value: 7} ]
  51. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1.Lex this line for identi f iers 2.Is this identi f ier is below a ‖? 3.Just-in-time de f ine that identi f ier to equal that value Identi f ier 'foo' spans from 0 to 2 vequals_to_process = [ {col: 1, value: 7} ]
  52. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1.Lex this line for identi f iers 2.Is this identi f ier is below a ‖? 3.Just-in-time de f ine that identi f ier to equal that value Identi f ier 'foo' spans from 0 to 2 vequals_to_process = [ {col: 1, value: 7} ] def #{identifier}(*args) = value
  53. @kkuchta High Level Approach 0 1 2 7 ‖ f

    o o Column Line 1.Lex this line for identi f iers 2.Is this identi f ier is below a ‖? 3.Just-in-time de f ine that identi f ier to equal that value Identi f ier 'foo' spans from 0 to 2 vequals_to_process = [ {col: 1, value: 7} ] def foo(*args) = 7
  54. @kkuchta High Level Approach 1. Look for vequals to process

    next line 2. Look for identi f iers that line up with vequals from the previous line
  55. @kkuchta Low Level Approach require 'ripper' class Vequals def enable

    @vequals_by_line = {} trace = TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] check_for_vequals(line, tp) process_any_vequals_from_previous_line(line, tp) end Object.define_method(:‖) {|*_|} trace.enable end def self.enable() Vequals.new.enable end def check_for_vequals(line, tp) @vequals_by_line[tp.lineno] = [] column = 0
  56. @kkuchta Low Level Approach def self.enable() Vequals.new.enable end def check_for_vequals(line,

    tp) @vequals_by_line[tp.lineno] = [] column = 0 lexed = Ripper.lex(line) lexed.each do |_positions, type, token, state| if type == :on_ident && token == "‖" exp_above = get_exp(tp.path, tp.lineno - 1, column) eval_result = tp.binding.eval(exp_above) @vequals_by_line[tp.lineno] << { value_to_assign: eval_result, column: column } end column += token.length end end def process_any_vequals_from_previous_line(line, tp) return unless vequals_to_process = @vequals_by_line[tp.lineno-1]
  57. @kkuchta Low Level Approach end column += token.length end end

    def process_any_vequals_from_previous_line(line, tp) return unless vequals_to_process = @vequals_by_line[tp.lineno-1] lexed = Ripper.lex(line) column = 0 lexed.each do |_positions, type, token, state| if type == :on_ident matching_vequals_entry = vequals_to_process.find do |vequals_entry| (column..(column + token.length)).include?(vequals_entry[:column]) end if matching_vequals_entry value_to_assign = matching_vequals_entry[:value_to_assign] if tp.binding.local_variable_defined?(token.to_sym) tp.binding.local_variable_set(token.to_sym, value_to_assign) else value_as_literal = literalize(value_to_assign) assignment_code = "def #{token}(*args) = #{value_as_literal}" tp.binding.eval(assignment_code) end end end column += token.length
  58. @kkuchta Low Level Approach require 'ripper' class Vequals def enable

    @vequals_by_line = {} trace = TracePoint.new(:line) do |tp| line = File.readlines(tp.path)[tp.lineno - 1] check_for_vequals(line, tp) process_any_vequals_from_previous_line(line, tp) end Object.define_method(:‖) {|*_|} trace.enable end def self.enable() Vequals.new.enable end def check_for_vequals(line, tp) @vequals_by_line[tp.lineno] = [] column = 0 lexed = Ripper.lex(line) lexed.each do |_positions, type, token, state| if type == :on_ident && token == "‖" exp_above = get_exp(tp.path, tp.lineno - 1, column) eval_result = tp.binding.eval(exp_above) @vequals_by_line[tp.lineno] << { value_to_assign: eval_result, column: column } end column += token.length end end def process_any_vequals_from_previous_line(line, tp) return unless vequals_to_process = @vequals_by_line[tp.lineno-1] lexed = Ripper.lex(line) column = 0 lexed.each do |_positions, type, token, state| if type == :on_ident matching_vequals_entry = vequals_to_process.find do | vequals_entry| (column..(column + token.length)).include? (vequals_entry[:column]) end if matching_vequals_entry value_to_assign = matching_vequals_entry[:value_to_assign] if tp.binding.local_variable_defined?(token.to_sym) tp.binding.local_variable_set(token.to_sym, value_to_assign) else value_as_literal = literalize(value_to_assign) assignment_code = "def #{token}(*args) = #{value_as_literal}" tp.binding.eval(assignment_code) end end end column += token.length end end def get_exp(path, lineno, column) line = File.readlines(path)[lineno - 1] exp_ranges = get_exp_ranges(line) exp_range = exp_ranges.find do |exp_range| exp_range[1].include?(column) end exp_range[0] end def get_exp_ranges(line) lexed = Ripper.lex(line) column = 0 exp_ranges = [] lexed.each do |_positions, type, token, state| case type when :on_tstring_content exp_ranges << ["'" + token + "'", (column..(column + token.length))] when :on_ident exp_ranges << [token, (column..(column + token.length))] when :on_int exp_ranges << [token, (column..(column + token.length))] else # Skipping type end column += token.length end exp_ranges end def literalize(value) if value.is_a? String '"' + value + '"' else value end end end
  59. @kkuchta It Works! Vequals.enable [7, 123] ‖ ‖ foo bar

    puts foo # prints 7! puts bar # prints 123!
  60. @kkuchta It Works! [3, 4, 5] ‖ ‖ ‖ a

    = b <= 6 ‖ ‖ d => c ‖ ‖ x == e ‖ ‖ f = g = h => z
  61. @kkuchta It Works! 1 ‖ x = x = x

    = x = x ‖ ‖ ‖ ‖ ‖ l = x = x = x = x ‖ ‖ ‖ ‖ ‖ l = x = x = x = l ‖ ‖ ‖ ‖ ‖ l = x = x = x = l ‖ ‖ i =___= i ‖ ‖ ‖ ‖ z z z z
  62. @kkuchta Exercises for the reader - Upward assignment - Diagonal

    assignment - Inward/outward assignment along the z axis - Forward and backwards in time assignment
  63. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  64. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  65. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? A: github.com/kkuchta/vequals "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  66. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? A: github.com/kkuchta/vequals Q: Should I use this in production? "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  67. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? A: github.com/kkuchta/vequals Q: Should I use this in production? A: Absolutely. "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  68. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? A: github.com/kkuchta/vequals Q: Should I use this in production? A: Absolutely.(Not you, Luke) "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  69. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? A: github.com/kkuchta/vequals Q: Should I use this in production? A: Absolutely. Q: I have a real question (Not you, Luke) "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  70. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? A: github.com/kkuchta/vequals Q: Should I use this in production? A: Absolutely. Q: I have a real question A: Right here! (Not you, Luke) "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd
  71. @kkuchta FAQ Q: Who's responsible for this mess A: Kevin

    Kuchta, @kkuchta, kevinkuchta.com Q: Where can I see this code in more detail? A: github.com/kkuchta/vequals Q: Should I use this in production? A: Absolutely. Q: I have a real question A: Right here! Q: Who in gods name hired you? (Not you, Luke) "foo" => bar ‖ aaaaa ‖ ‖ c = b ddd