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

Exploring #to_proc

Exploring #to_proc

A look at how some "odd syntax" introduced in Rails 1.1 and then added to Ruby itself might not solely be for the sake of convenience but hint at a more powerful idea seen in other programming languages. Presented at the March 2015 London Ruby User Group.

Paul Mucur

March 09, 2015
Tweet

More Decks by Paul Mucur

Other Decks in Technology

Transcript

  1. Exploring #to_proc

  2. Paul Mucur @mudge

  3. [:foo,  :bar].map(&:to_s)

  4. [:foo,  :bar].map(&:to_s)

  5. [1,  2,  3]  #=>  ["1",  "2",  "3"]

  6. result  =  []   [1,  2,  3].each  do  |number|  

       result  <<  number.to_s   end
  7. [1,  2,  3].map  do  |number|      number.to_s   end

  8. [1,  2,  3].map  {  |number|  number.to_s  }

  9. None
  10. [1,  2,  3].map(&:to_s)

  11. None
  12. [1,  2,  3].map(&:to_s)

  13. [1,  2,  3].map(:to_s)

  14. “When you say names.map(&xxx), you’re telling Ruby to pass the

    Proc object in xxx to the map method as a block. If xxx isn’t already a Proc object, Ruby tries to coerce it into one by sending it a to_proc message.” — Dave Thomas, “Programming Ruby”
  15. [1,  2,  3].map(&:to_s.to_proc)

  16. :to_s.to_proc

  17. class  Symbol      def  to_proc        

     Proc.new  {  |obj|  obj.send(self)  }      end   end
  18. Proc.new  {  |obj|  obj.send(:to_s)  }

  19. Proc.new  {  |obj|  obj.to_s  }

  20. [1,  2,  3].map(&Proc.new  {  |obj|  obj.to_s  })

  21. [1,  2,  3].map  {  |obj|  obj.to_s  }

  22. [1,  2,  3].map  {  |number|  number.to_s  }

  23. http://railscasts.com/episodes/6-shortcut-blocks-with-symbol-to-proc

  24. http://railscasts.com/episodes/6-shortcut-blocks-with-symbol-to-proc

  25. “Elegance & familiarity are orthogonal.” — Rich Hickey

  26. http://thepugautomatic.com/2014/11/array-to-proc-for-hash-access/

  27. http://thepugautomatic.com/2014/11/array-to-proc-for-hash-access/

  28. “Maps are functions of their keys.” ! http://clojure.org/data_structures#Data Structures-Keywords

  29. “[Hashes] are functions of their keys.” ! http://clojure.org/data_structures#Data Structures-Keywords

  30. Input Output Function

  31. Domain Co-domain 1 2 3 A B C

  32. Hashes ! {:name  "Bob",  :age  42} {name:  "Bob",  age:  42}

  33. Key Value Function

  34. :name "Bob" Function

  35. Domain Co-domain :name :age "Bob" 42

  36. (def  person  {:name  "Bob",  :age  42})   ! ! !

    ! !
  37. (def  person  {:name  "Bob",  :age  42})   ! (person  :name)

      ! ! !
  38. (def  person  {:name  "Bob",  :age  42})   ! (person  :name)

      ;;=>  "Bob"   ! !
  39. (def  person  {:name  "Bob",  :age  42})   ! (person  :name)

      ;;=>  "Bob"   ! (person  :height)  
  40. (def  person  {:name  "Bob",  :age  42})   ! (person  :name)

      ;;=>  "Bob"   ! (person  :height)   ;;=>  nil
  41. (map  even?  [1  2  3  4])  

  42. (map  even?  [1  2  3  4])   ;;=>  (false  true

     false  true)
  43. (map  person  [:name  :age])  

  44. (map  person  [:name  :age])   ;;=>  ("Bob"  42)

  45. person  =  {  name:  "Bob",  age:  42  }   !

    !
  46. person  =  {  name:  "Bob",  age:  42  }   !

    name,  age  =  [:name,  :age].map(&person)  
  47. person  =  {  name:  "Bob",  age:  42  }   !

    name,  age  =  [:name,  :age].map(&person)   #=>  ["Bob",  42]
  48. [:yes,  :yes,  :no,  :yes].map(&{yes:  1,  no:  0})   http://blog.japila.pl/2011/01/maps-as-data-structures-in-clojure-are-functions-of-their-keys-a-nifty-use-case/

  49. [:yes,  :yes,  :no,  :yes].map(&{yes:  1,  no:  0})   #=>  [1,

     1,  0,  1] http://blog.japila.pl/2011/01/maps-as-data-structures-in-clojure-are-functions-of-their-keys-a-nifty-use-case/
  50. class  Hash      def  to_proc        

     Proc.new  {  |key|  self[key]  }      end   end
  51. class  Hash      def  to_proc        

     proc  {  |key|  self[key]  }!    end   end
  52. class  Hash      def  to_proc        

     method(:[]).to_proc      end   end
  53. Arrays ! ["A"  "B"  "C"] ["A",  "B",  "C"]

  54. Index Value Function

  55. 0 "A" Function

  56. Domain Co-domain 0 1 2 "A" "B" "C"

  57. (def  letters  ["A"  "B"  "C"])   ! (letters  1)  

  58. (def  letters  ["A"  "B"  "C"])   ! (letters  1)  

    ;;=>  "B"
  59. (map  letters  (range  1  3))  

  60. (map  letters  (range  1  3))   ;;=>  ("B"  "C")

  61. letters  =  ["A",  "B",  "C"]   ! (1...3).map(&letters)  

  62. letters  =  ["A",  "B",  "C"]   ! (1...3).map(&letters)   #=>

     ["B",  "C"]
  63. class  Array      def  to_proc        

     method(:[]).to_proc      end   end
  64. “Sets are functions of their members.” ! http://clojure.org/data_structures#Data Structures-Keywords

  65. Sets ! #{"Steve"  "Michael"} Set["Steve",  "Michael"]

  66. Member Member Function

  67. "Steve" "Steve" Function

  68. "Arnold" nil Function

  69. Domain Co-domain "Steve" "Michael" "Steve" "Michael"

  70. (def  banned  #{"Steve"  "Michael"})   (def  guest-­‐list  ["Brian"  "Josh"  "Steve"])

      ! ! http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html
  71. (def  banned  #{"Steve"  "Michael"})   (def  guest-­‐list  ["Brian"  "Josh"  "Steve"])

      ! (remove  banned  guest-­‐list)   http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html
  72. (def  banned  #{"Steve"  "Michael"})   (def  guest-­‐list  ["Brian"  "Josh"  "Steve"])

      ! (remove  banned  guest-­‐list)   ;;=>  ("Brian"  "Josh") http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html
  73. banned  =  Set["Steve",  "Michael"]   guest_list  =  ["Brian",  "Josh",  "Steve"]

      ! !
  74. banned  =  Set["Steve",  "Michael"]   guest_list  =  ["Brian",  "Josh",  "Steve"]

      ! guest_list.reject(&banned)  
  75. banned  =  Set["Steve",  "Michael"]   guest_list  =  ["Brian",  "Josh",  "Steve"]

      ! guest_list.reject(&banned)   #=>  ["Brian",  "Josh"]
  76. class  Set      def  to_proc        

     proc  {  |element|  element  if  member?(element)  }      end   end
  77. Why?

  78. http://codon.com/a-lever-for-the-mind

  79. None
  80. (g  ∘  f  )(x) = g(f(x))

  81. http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html old_orders  =  {  1  =>  44,  2  =>  33

     }   new_orders  =  {  1  =>  55,  2  =>  33  }   ! ! ! ! ! ! ! ! ! !
  82. http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html old_orders  =  {  1  =>  44,  2  =>  33

     }   new_orders  =  {  1  =>  55,  2  =>  33  }   ! require  'active_support/core_ext/hash/diff'   old_orders.diff(new_orders).keys   #=>  [1]   ! ! ! ! ! !
  83. http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html old_orders  =  {  1  =>  44,  2  =>  33

     }   new_orders  =  {  1  =>  55,  2  =>  33  }   ! old_orders.dup.      delete_if  {  |k,  v|  new_orders[k]  ==  v  }.      merge!(new_orders.dup.delete_if  {  |k,  v|  old_orders.has_key?(k)  }).keys   #=>  [1]   ! ! ! ! !
  84. http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html old_orders  =  {  1  =>  44,  2  =>  33

     }   new_orders  =  {  1  =>  55,  2  =>  33  }   ! order_differences  =  old_orders.merge(new_orders)  {  |_key,  oldval,  newval|      oldval  !=  newval   }   #=>  {1=>true,  2=>false}   ! ! ! ! !
  85. http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html old_orders  =  {  1  =>  44,  2  =>  33

     }   new_orders  =  {  1  =>  55,  2  =>  33  }   ! order_differences  =  old_orders.merge(new_orders)  {  |_key,  oldval,  newval|      oldval  !=  newval   }   #=>  {1=>true,  2=>false}   ! order_ids  =  new_orders.keys   #=>  [1,  2]   ! !
  86. http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html old_orders  =  {  1  =>  44,  2  =>  33

     }   new_orders  =  {  1  =>  55,  2  =>  33  }   ! order_differences  =  old_orders.merge(new_orders)  {  |_key,  oldval,  newval|      oldval  !=  newval   }   #=>  {1=>true,  2=>false}   ! order_ids  =  new_orders.keys   #=>  [1,  2]   ! order_ids.select(&order_differences)   #=>  [1]
  87. http://blog.jayfields.com/2010/08/clojure-using-sets-and-maps-as.html old_orders  =  {  1  =>  44,  2  =>  33

     }   new_orders  =  {  1  =>  55,  2  =>  33  }   ! Hash[new_orders.to_a  -­‐  old_orders.to_a].keys   #=>  [1]   ! ! ! ! ! ! !
  88. One more thing…

  89. [1,  3,  4,  2].sort_by(&:-­‐@)   https://twitter.com/threedaymonk

  90. [1,  3,  4,  2].sort_by(&:-­‐@)   #=>  [4,  3,  2,  1]

    https://twitter.com/threedaymonk
  91. http://raganwald.com/2007/11/fun-with-symboltoproc.html

  92. http://mudge.name/2014/11/26/data-structures-as-functions.html

  93. Thank you https://speakerdeck.com/mudge/exploring-number-to-proc