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

Vertical Assignment in Ruby

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Kevin Kuchta 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).

Avatar for Kevin Kuchta

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