Issue #6284 has been updated by rosenfeld (Rodrigo Rosenfeld Rosas).


In Math multiplication is always associative, even for matrix. I.e: (A*B)*C == A*(B*C). If we use * for ??? (composition) it resembles multiplication. Function composition is analog to matrix multiplication which are commonly used for transformation compositions as well. In fact, function composition is also associative.

So, when representing h = f ??? g as h = f * g it makes sense to me (although Math preferring a different symbol for multiplication and composition is a good indication that we should consider this as well for Ruby - more on that later on). But Math representation is procedural, not object oriented. If we try to mix both approaches to fit Ruby philosophy this could lead to great confusion.

Ruby can be also used for procedural programming:

sqrt = ->(n){ Math.sqrt n } # Although I agree that (n)->{} would read more natural to me, just like in CoffeeScript
square_sum = ->(a, b) { a*a + b*b }
hypotenuse = sqrt * square_sum
5 == hypotenuse.call 3, 4 # equivalent to: sqrt.call square_sum.call 3, 4

This makes total sense to me using procedural notation. I'm not sure how would someone use this using some OO notation instead...

Now with regards to composition notation, I think a different notation could help those reading some code and trying to understand it. Suppose this method:

def bad_name(bad_argument_name, b)
  bad_argument_name * b # or bad_argument_name << b
end

You can't know beforehand if bad_argument_name is an array, a number or a proc/lambda. If we read this instead:

def bad_name(bad_argument_name, b)
  bad_argument_name <- b
end

we would then have a clear indication that bad_argument_name is probably a proc/lambda. I know the same argument could be used to differentiate << between strings and arrays among other cases. But I think that function composition is conceptually much different from those other operations (concatenation, multiplication) than concatenation (<<) is for strings and arrays. In both cases we are concatenating but concatenation means different things for strings and arrays in non surprising ways.

But then using this arrow notation I would expect that (a <- b) would mean "a before b" (b(a(...))) while (a ??? b) means "a after b" (a(b(...))).

I find it a bit awful to use "hypotenuse = square_sum <- sqrt", although it is the way OO usually work ([4, 5].square_num.sqrt - pseudo-code of course). But we would not be using "[4, 5].hypotenuse", but "hypotenuse.call 4, 5", right? So, since we're using procedural notation for procs/lambdas we should be thinking of procedural programming when deciding which operator to use.

I would really prefer to have lambda syntax as "double = <-{|n| n * 2}" and function composition as "hypotenuse = sqrt -> square_sum" (sqrt after square_sum). But since I don't believe the lambda syntax won't ever change, let's try to see this over a different perspective.

Instead of reading (a <- b) as "a before b", I'll try to think of it as being "b applied to a" (a(b(...))). This also make sense to me so I can easily get used to this. It would work the same way as "*" but there would be a clear indication that this refers to function composition rather than some generic multiplication algorithm.

Having said that, I'd like to confirm that I'm ok with either * or <- and I'd really like to have function composition as part of Ruby.
----------------------------------------
Feature #6284: Add composition for procs
https://bugs.ruby-lang.org/issues/6284#change-32748

Author: pabloh (Pablo Herrero)
Status: Feedback
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: 
Target version: 2.0.0


=begin
It would be nice to be able to compose procs like functions in functional programming languages:

    to_camel = :capitalize.to_proc
    add_header = ->val {"Title: " + val}

    format_as_title = add_header << to_camel << :strip

instead of:

    format_as_title = lambda {|val| "Title: " + val.strip.capitalize }


It's pretty easy to implement in pure ruby:

  class Proc
    def << block
      proc { |*args| self.call( block.to_proc.call(*args) ) }
    end
  end
=end


-- 
http://bugs.ruby-lang.org/