{"id":221,"date":"2013-03-12T14:03:44","date_gmt":"2013-03-12T20:03:44","guid":{"rendered":"http:\/\/sha.nnoncarey.com\/blog\/?p=221"},"modified":"2013-03-12T14:05:08","modified_gmt":"2013-03-12T20:05:08","slug":"reducers-a-la-rich-hickley-in-ruby","status":"publish","type":"post","link":"https:\/\/sha.nnoncarey.com\/blog\/archives\/221","title":{"rendered":"Reducers a la Rich Hickley in Ruby"},"content":{"rendered":"<p>This is a draft of an article which translates Rich Hickley&#8217;s &#8220;reducers&#8221; concept into the Ruby language.<\/p>\n<p><iframe loading=\"lazy\" src=\"https:\/\/player.vimeo.com\/video\/45561411\" width=\"840\" height=\"473\" frameborder=\"0\" title=\"Reducers - Rich Hickey\" webkitallowfullscreen mozallowfullscreen allowfullscreen><\/iframe><br \/>\nhttp:\/\/clojure.com\/blog\/2012\/05\/08\/reducers-a-library-and-model-for-collection-processing.html<\/p>\n<p>collect, aka map, does:<br \/>\n1. Recursion (apply function to head, then call map recursively on the tail)<br \/>\n2. Consumes an ordered list (guarantees order of execution)<br \/>\n3. Produces an ordered list<\/p>\n<p>reduce, as currently provided in Clojure, is ignorant of the collection structure &#038; allows the collection to implement the coll-reduce protocol. Also, it can produce anything: an aggregate, a map, etc. Still inherently sequential (monotonic) in the order of the operations (left fold with seed).<\/p>\n<p>Don&#8217;t create a new collection when we call map or filter\u00e2\u20ac\u00a6 don&#8217;t add your result to the collection inside the reduce\u00e2\u20ac\u00a6<\/p>\n<p>How to produce a result? A Reduction Transformer.<\/p>\n<p>We want to be able to create a recipe for creating pies, maybe even before we have a bag of apples. This implies being able to compose multiple reductions. Composing might look something like: map(take_sticker_off_apple).map(reject_bad_apples)<\/p>\n<p>reducing function: function you pass to reduce\u00e2\u20ac\u00a6 a binary operator, such as addition. Arguments don&#8217;t have to be symmetric: order is important. First arg is seed\/result\/accumulator\u00e2\u20ac\u00a6 second arg is input to process that&#8217;s building the result.<br \/>\nmapping a function: you have a reducing function<\/p>\n<pre class=\"brush: ruby; title: ; notranslate\" title=\"\">\r\n\/\/f1 is the reducing function (a binary operator, etc. see above)\r\n\/\/ f(input) is &quot;mess with that input&quot;\r\nmapping(f) { \/\/ this is a reduction transformer\r\n -&gt;(f1) {\r\n  -&gt;(result, input) {\r\n    f1.call(result, f.call(input)\r\n  }\r\n}\r\n\r\ndo_stuff_to_input(input) {\r\n  input.label = nil\r\n  return input\r\n}\r\nfilter_mapping = mapping(do_stuff_to_input) #we have bound f\r\n\r\nreducing_function(result, input) {\r\n  return result + input\r\n}\r\nmodified_reducing_function = filter_mapping.call(reduction_transformer) #we have bound f1\r\n\r\nbag_of_apples.reduce(modified_reducing_function)\r\n<\/pre>\n<p>Mapping case: 1->1 case<br \/>\nFiltering case: 1->1\/0<\/p>\n<pre class=\"brush: ruby; title: ; notranslate\" title=\"\">\r\nfiltering(predicate_fn) { \/\/ this is a reduction transformer\r\n  -&gt;(reducing_fn) {\r\n    -&gt;(result, input) {\r\n      if(predicate_fn.call(input)) {\r\n        return reducing_fn.call(result, input)\r\n      else\r\n        return result\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\nbag_of_apples.reduce(\r\n  filtering(is_good_apple?) \/\/predicate_fn is bound\r\n    .call(array_concat) \/\/reducing_fn is bound\r\n)\r\n<\/pre>\n<p>takes a reducing function, returns one, and if predicate is true call reducing function on the result with the new input. What if predicate false? Just don&#8217;t touch the result. This is the right way to write filter.<\/p>\n<p>One to many case: 1->M eg. mapcat<\/p>\n<pre class=\"brush: ruby; title: ; notranslate\" title=\"\">\r\nmapcatting(f) {\r\n  -&gt;(reducing_fn) {\r\n    -&gt;(result, input) {\r\n      reduce(reducing_fn, result, f.call(input))\r\n    }\r\n  }\r\n}\r\n\r\nbag_of_apples.reduce(\r\n  mapcatting(slice_apple_into_pieces(5)).call(array_concat)\r\n)\r\n<\/pre>\n<p>There&#8217;s a lot of repeated stuff\u00e2\u20ac\u00a6 we can make a macro to reduce it down to the minimum.<br \/>\nIt&#8217;s also not pretty. Function that returns a function, for example.<\/p>\n<p>Also, this is not the cake.<\/p>\n<p>TO BE CONTINUED<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a draft of an article which translates Rich Hickley&#8217;s &#8220;reducers&#8221; concept into the Ruby language. http:\/\/clojure.com\/blog\/2012\/05\/08\/reducers-a-library-and-model-for-collection-processing.html collect, aka map, does: 1. Recursion (apply function to head, then call map recursively on the tail) 2. Consumes an ordered list (guarantees order of execution) 3. Produces an ordered list reduce, as currently provided in Clojure, &hellip; <a href=\"https:\/\/sha.nnoncarey.com\/blog\/archives\/221\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Reducers a la Rich Hickley in Ruby&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-221","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts\/221","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/comments?post=221"}],"version-history":[{"count":13,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts\/221\/revisions"}],"predecessor-version":[{"id":261,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/posts\/221\/revisions\/261"}],"wp:attachment":[{"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/media?parent=221"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/categories?post=221"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sha.nnoncarey.com\/blog\/wp-json\/wp\/v2\/tags?post=221"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}