From: Intransition Date: 2012-06-17T07:47:34+09:00 Subject: Enumerable#every, the Functor and Ruby support for HOM ------=_Part_282_8002221.1339886851114 Content-Type: multipart/alternative; boundary="----=_Part_283_7168630.1339886851114" ------=_Part_283_7168630.1339886851114 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Have you heard of `Enumerable#every`? It lets you apply a method call to all elements of an enumerable. Check it out... [1,2,3].every + 3 #=> [4,5,6] [1,2,3].every * 3 #=> [3,6,9] words = ["hello", "world"] words.every.upcase! words #=> ["HELLO", "WORLD"] Essentially every is a convenient and nicely readable "fluent notation" alternative to using a map/collect block. Now you might be wondering how this bit of magic is achieved? Here is the definition. module Enumerable def every Functor.new do |op,*args| map{ |a| a.send(op,*args) } end end end As you can see it works via something called a "Functor". Generally speaking, a "functor" is simply an object that encapsulates a function. In this case I have used the term as a concise reference to a specialized form of such to achieve Higher Order Messaging. The basic definition of this class is: class Functor def initialize(&function) @function = function end def method_missing(op, *args, &blk) @function.call(op, *args, &blk) end end This Functor class can be used in many many ways. Enumerable#every is just one of many examples. In fact, Ruby's own Enumerator class is actually just a highly specialized type of Functor. As useful as a Functor can be, it does however have two downsides. First it depends on #method_missing. This means public Object methods can interfere with its usefulness. This can be worked around by making Functor a subclass of BasicObject. Though a few public methods remain, this is good enough for all practical purposes. A more problematic issue is the fact that it requires the creation of an indeterminate object every time the a method using a Functor is called. This is pretty inefficient, slows things down and requires us to be memory conscious. Caching can be used in some cases, but rarely is it an ideal fix. Recently I came up with an idea that would allow these issues to be circumvented entirely. I realized that since this is a higher order *message*, then really it would be best handled as a messaging issue --hence via the definition of a "higher order method". In other words if Ruby had built-in support for the concept, the intermediate object would not be necessary and consequently interfering public methods would not exist. To facilitate this, I came up with a potential notation. Here's how it would be used to define Enumerable#every: module Enumerable def every => op, *args map{ |a| a.send(op,*args) } end end (Hmm... as I typed this it occurs to me that maybe `=>` would be more intuitive if it pointed the other way using `<=`.) Regardless of the actual notation used, the idea is to allow Ruby to handle higher-order-messages internally as a special type of method. For reference here are a few other examples of using Functor: * https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/file.rb * https://github.com/rubyworks/facets/blob/master/lib/core/facets/kernel/ergo.rb * https://github.com/rubyworks/facets/blob/master/lib/core/facets/kernel/not.rb P.S. There is also a functor gem out there, but it is not the Functor as defined above. The functor gem came along well after the one described here (which is included with Ruby Facets). The gems's implementation is basically a kind of struct that supports multiple dispatch capabilities. The above is much more generic and actually can even be used to create the functor gem's more specialized kind with just a bit of extra code. ------=_Part_283_7168630.1339886851114 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
Have you heard of `Enumerable#every`? It lets you apply a method call = to all elements of an enumerable. Check it out...

=   [1,2,3].every + 3  #=3D> [4,5,6]

  [1,2,3].every * 3  #=3D> [3,6,9]

<= div>  words =3D ["hello", "world"]
  words.every.upcase= !
  words  #=3D> ["HELLO", "WORLD"]

Essentially every is a convenient and nicely readable "fluent nota= tion" alternative to using a map/collect block.

No= w you might be wondering how this bit of magic is achieved? Here is the def= inition.

    module Enumerable
       def every
    &nbs= p;     Functor.new do |op,*args|
      &= nbsp;     map{ |a| a.send(op,*args) }
    &nb= sp;     end
       end
&n= bsp;   end

As you can see it works via = something called a "Functor". Generally speaking, a "functor" is simply an = object that encapsulates a function. In this case I have used the term as a= concise reference to a specialized form of such to achieve <a href= =3D"http://en= .wikipedia.org/wiki/Higher_order_message">Higher Order Messaging<= /a>. The basic definition of this class is:

&nb= sp; class Functor
    def initialize(&function)
      @function =3D function
  &nb= sp; end
    def method_missing(op, *args, &blk)
=
      @function.call(op, *args, &blk)
    end
  end

This Fun= ctor class can be used in many many ways. Enumerable#every is just one of m= any examples. In fact, Ruby's own Enumerator class is actually just a highl= y specialized type of Functor.

As useful as a Func= tor can be, it does however have two downsides. First it depends on #method= _missing. This means public Object methods can interfere with its= usefulness. This can be worked around by making Functor a subclass of Basi= cObject. Though a few public methods remain, this is good enough for all pr= actical purposes. A more problematic issue is the fact that it requires the= creation of an indeterminate object every time the a method using a F= unctor is called. This is pretty inefficient, slows things down and require= s us to be memory conscious. Caching can be used in some cases, but rarely = is it an ideal fix.

Recently I came up with an ide= a that would allow these issues to be circumvented entirely. I realized tha= t since this is a higher order *message*, then really it would be best hand= led as a messaging issue --hence via the definition of a "higher order meth= od". In other words if Ruby had built-in support for the concept, the inter= mediate object would not be necessary and consequently interfering&nbs= p;public methods would not exist. To facilitate this, I came up with a pote= ntial notation. Here's how it would be used to define Enumerable#every:

    module Enumerable
  &nbs= p;   def every =3D> op, *args
       =  map{ |a| a.send(op,*args) }
      end
<= div>    end

(Hmm... as I typed this it o= ccurs to me that maybe `=3D>` would be more intuitive if it pointed the = other way using `<=3D`.) Regardless of the actual notation used, the ide= a is to allow Ruby to handle higher-order-messages internally as a special = type of method.

For reference here are a few other= examples of using Functor:
* https://github= .com/rubyworks/facets/blob/master/lib/core/facets/string/file.rb

P.S. There is also a functor gem out th= ere, but it is not the Functor as defined above. The functor gem came along= well after the one described here (which is included with Ruby Facets). Th= e gems's implementation is basically a kind of struct that suppor= ts multiple dispatch capabilities. The above is much more generic and = actually can even be used to create the functor gem's more specialized kind= with just a bit of extra code.

------=_Part_283_7168630.1339886851114-- ------=_Part_282_8002221.1339886851114--