Hal Fulton wrote:
> Ville Vainio wrote:
>> That is, string can know about sequences, but a sequence, being a more
>> general data type, should not know about strings.
 >
> I'd have to disagree. join, since it produces a string, obviously
> knows about strings even though it is not a string method.

I think Ville means that making join a method on a list means that lists 
must now know about strings.  Unfortunately, removing join as a list 
method still leaves lists knowledgeable about string for every object 
implements a to_s method.

In fact, knowing nothing but that to_s exists, you can write join like 
this ...

     def join(delimit)
       inject { |a, b| a.to_s + delimit + b.to_s }
     end

I have also heard (on c.l.py) that one reason the " ".join() form was 
chosen was to allow join to be polymorphic on string type.  This would 
allow join to be implemented differently in subclasses of string (such 
as a UnicodeString class perhaps).  Since join can be defined in terms 
of existing list methods (as we did above), there is no need to make 
polymorphic on lists or sequences.

However, making join a string method is neither necessary nor sufficient 
for a polymorphic behavior string subclass to be used.

For me, the big reason to make join a method in list is readability. 
Allowing methods chains that read linearly left to right is a big plus, 
overriding the other considerations in this case. (YMMV)

BTW, neither form is "more OO"  than the other.

>> As far as FP goes, allowing t.split w/o () is extremely non-FP,
>> because you lose first class functions. And FP without first class
>> functions is, well, Java.
> 
> I don't know FP either, but I thought method chaining was an important
> part of it. Because of the disconnect I mentioned earlier (where join
> is not actually acting on its receiver), the previous example doesn't
> show true method chaining.

Neither Ruby nor Python are functional languages, however both exhibit 
some features that are found in function languages.

When people think of features of functional languages, they primarily 
refer to two features in particular ..

(1) Stateless programming
(2) Higher order functions

Stateless programming means writing code that calculates a result 
without changing the state of the program as it executes.  This style of 
programming has many advantages, but neither Ruby nor Python adheres to 
this style, except in small snippets of code.

A functional language treats functions as first class data.  This means 
that functions can be passed about as data, and that operations can be 
performed on these functions that produce new functions.  A higher order 
function (in my perhaps flawed understanding) is a function that takes 
other functions as operands.

Here's a concrete example ... Suppose you had a function f(x,y) that 
returns the value x+y.  Now write a another function curry(f,n) that 
returns a brand-new function object.  This new function should take one 
argument (x) and return the value x+n (where n was the value passed to 
curry).

If your language can write functions like curry, then you have first 
class functions and higher order functions.  And this can be done in 
both Ruby and Python.

[... from an earlier message ...]
 >> actually, I tend to think Ruby's OO
 >> is crippled by not having functions as first class objects.

I find it interesting when people claim that Ruby has no first class 
functions, mostly because Ruby doesn't have functions in the first 
place.  What looks like functions are really methods on objects. 
Methods exist inside an object and like the proverbial quark, are never 
seen outside of an object.  Unlike functions which are explicitly 
called, methods are always invoked by an object in response to receiving 
message consisting of a message selector and a list of arguments. 
Sometimes we abbreviate and pretend we are calling functions, but it is 
still objects, methods and messages[1].

Question: If Ruby methods aren't function, how can we do functional 
programming?
Answer: With a poor man's function!

At this point it is useful to quote Anton van Straaten where he sums up 
his own enlightenment with these koan (see http://tinyurl.com/2m7p9):

     The venerable master Qc Na was walking with his student, Anton.
     Hoping to prompt the master into a discussion, Anton said "Master, I
     have heard that objects are a very good thing - is this true?" Qc Na
     looked pityingly at his student and replied, "Foolish pupil -
     objects are merely a poor man's closures."

     Chastised, Anton took his leave from his master and returned to his
     cell, intent on studying closures. He carefully read the entire
     "Lambda: The Ultimate..." series of papers and its cousins, and
     implemented a small Scheme interpreter with a closure-based object
     system. He learned much, and looked forward to informing his master
     of his progress.

     On his next walk with Qc Na, Anton attempted to impress his master
     by saying "Master, I have diligently studied the matter, and now
     understand that objects are truly a poor man's closures." Qc Na
     responded by hitting Anton with his stick, saying "When will you
     learn? Closures are a poor man's object." At that moment, Anton
     became enlightened.

If objects are a poor man's closure, they certainly can be a poor man's 
function as well.  In Ruby, we use objects as functions.  Any object 
that responds to the call message can be used, but by far the most 
common functional object is the Ruby Proc/Lambda object.

Here's the curry operation mentioned above, but done with first class 
function objects in Ruby.

    f = lambda { |x, y| x + y }
    curry = lambda { |f, n|   lambda { |x| f.call(n, x) } }
    g = curry.call(f, 2)
    g.call(3)   #=> 5

In summary:

o "def" defines a method, not a function, first class or otherwise.
o What looks like a function call is really a message send to an object.
o Ruby uses objects for its functional programming.  Since the function
   is an object, calling it involves a normal object-style message using
   "call" as the message selector.

I'll close with this thought.  Ruby and Python are perfect examples of a 
duality between functions and objects.  Where Ruby begins with objects 
and builds functions out of them, Python begins with functions and 
builds objects out of them.  And what is really surprising is that 
despite the rather fundamental differences in their semantic foundation, 
the languages are more alike than they are different!

-- 
-- Jim Weirich    jim / weirichhouse.org     http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

[1] I'm not trying to imply that all method invocations involve the
     reification of a message.  In fact, I have little knowledge of what
     goes on under the covers in the C implementation.  I'm only saying
     that obj.method is semantically equivalent to obj.send(:method).