Save 37% off PRO during our Black Friday Sale! »

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.

Beb7f5dd655d8b8e9093ef4fc5e59b6e?s=128

Paul Mucur

March 09, 2015
Tweet

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