raganwald
(This is a snapshot of my old weblog. New posts and selected republished essays can be found at raganwald.com.)

Monday, February 25, 2008
  (1..100).inject(&:+)


I want to show you my favourite line of ruby code:1, 2

(1..100).inject(&:+)
This code works out of the box in Ruby 1.9. This also works in Ruby 1.8 with Rails, thanks to this chunk of code called “Symbol#to_proc”:

class Symbol
# Turns the symbol into a simple proc, which is especially useful for enumerations.
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
Now, I should explain what I like about it. No, we aren’t going to debate whether { |acc, n| acc + n } contains accidental complexity. We are not going to talk about why this is better than whatever you would do in PHP or Visual Basic to sum the integers. Let go of any idea that this article is about golf. I like this line of code because it says a lot about how the Ruby language works. I do not want to talk about those other things, they are not interesting to either of us.

The two things I do want to talk about are that: 1. Somebody made Symbol#to_proc in Ruby 1.8, and that 2. It became part of Ruby 1.9. Let’s start with how it works.

Symbol#to_proc works because Symbol is an Open Class in Ruby. And Ruby is Dynamically Typed, meaning you can modify classes and objects at run time, thus Symbol gets the to_proc method added when you run the above code. (You sometimes see the expression “Dynamic Typing” used to describe a system where you don’t need to write the type of an object before using it. I call that “Latent Typing” and use the word “Dynamic” to describe a system where types can change thanks to adding or removing methods. In this case, we are adding a method to every Symbol in a program, which is pretty dynamic.)

The Acid Test

One of the things that really intrigues me this little snippet of code, (1..100).inject(&:+), is that you can tell an awful lot about someone’s attitude towards programming styles, languages, and cultural bias by asking them what they think of it. It touches on functional programming, on brevity, and in this case, on open classes.

Take a moment to really think this through. What do you think of the fact that someone sat down and modified one of Ruby’s most core classes, Symbol?

This is a decidedly non-trivial thing to do. Changing Symbol in any way opens up a Pandora’s box of risk. You can break a lot of code. You can—I beg your pardon, you will—confuse people who have never heard of Symbol#to_proc. Just so you can express “Fold the numbers from one to one hundred using addition” in a very direct manner eliminating the boilerplate of { |a, n| a + n }.

If Symbol wasn’t an open class, how would inject(&:+) have been added to the language? Open classes and other “turtles all the way down” features are dangerous. But dangerous features help a language evolve. Lisp’s macros are extremely dangerous. And they are also what made it possible to discover/invent CLOS. Had there been no macros, the only way to experiment with new ideas in OO would be to create whole new language, an exercise in Accidental Complexity.




The Ruby Way is the perfect second Ruby book for serious programmers. The Ruby Way contains more than four hundred examples explaining how to do everything from distribute Ruby with Rinda to functional programming techniques.

When you look at a language like Java, you see this ponderous, bureaucratic process for language change. Java does not include dangerous features like macros or open classes, and as a result change can only come from the language’s implementation, not from out on the fringe where people are using it and creating their own features to address their own needs.

(As someone pointed out, not all languages with centralized control evolve as slowly as Java. But they still are centralized, they still impose a vision from the top. The point is about central planning vs. the free market if you to toss another metaphor on the pile.)

Lispers talk about Bottom-Up Programming. Well, dangerous features enable bottom-up language evolution. We discovered we like Symbol#to_proc because it bubbled up from the bottom. Someone invents something. If other people like it, they use it. The word gets around. People improve on it. Eventually it gains acceptance and becomes the de facto way to write code.

This is true in all languages, but languages—like Ruby—that include dangerous features give the fringe a broader latitude to invent new things. Of course, they also break things and they invent stupid things and they get excited and write entire applications by patching core classes instead of writing new classes and commit all sorts of sin.

The number of readers who seem unable to get past the literal syntax-as-solution of (1..100).inject(&:+) and grasp that the interesting bit isn’t the literal utility of that line of code, or that there are others ways to implement the same functionality, or that pet language X has an easier/smaller/cleaner mechanism for summing integers, or that there are “better” lines of “cooler” code, is really quite depressing to me.

Is it really so difficult to work out that the interesting bits are in the mere existence—or not—of that code at all, per the developer’s whim?
—Chris Cummer

But they will also invent Symbol#to_proc. The “marketplace” has voted very strongly in favour of Symbol#to_proc, so much so that it is now in the next revision of the language. Maybe you love Symbol#to_proc, maybe you hate it. But somebody loves it, enough to have created it, and enough other people liked it that you can find implementations everywhere. So it clearly has some appeal. But that isn’t the point. Even if it is a terrible feature, it is a feature invented by the people and for the people, not a feature pushed down from above.

Open classes makes invention possible in real time. No committee. No waiting for the next language revision. If you think of it, you can try it. Does Symbol#to_proc make it worthwhile? How about andand? Why don’t you tell me? I was serious when I asked you what you think of it.

Are you young at heart?

For some, languages are best when they are mature, when there is nothing left to discover, when all of the “best practices” have been laid down in stone. There’s a comforting stability, a knowledge that some hot shot they hire tomorrow will not arrive with a bunch of new techniques you have never heard of. In mature, stable environments the people around the longest have the most authority.

When anybody can invent a new thing, it’s the youngsters who hold the power. If you don’t like the way Ruby handles nil, you can write your own andand. Surprise surprise, so did half a dozen other people, so for the moment you’re going to find that there is no clear standard, no “One obvious way to do it” in Ruby. That kind of thing is painful for a certain type of personality: they want to know “Which way is the best.” Ideally, they want Matz (or as one person commented, Guido) to anoint one of these the “winner” by building it into the core. That way the oldsters get the power back, they have the experience so they have memorized more of the core API.

Invention isn’t its own reward. Creating new stuff for the sake of novelty wears thin pretty quickly. But when you are making up the rules, you get to stack them in your favour. If you create it, it works for your needs. Naturally, there is an argument that the mature, tested heavyweight whatsis is better than your half-baked roll-your-own whatsis. or at least, there is an argument if we are talking about an Enterprise Transactional Data Processing Platform.

But look at Rails. It is much smaller than something like Spring. So it was much better for 37 Signals than Spring, even though they rolled their own. Rolling your own is a lot harder in a language—like Java—that doesn’t let you have the dangerous features. The trade-offs between using the old and inventing the new are different in a language like Ruby. You still want to use what already works most of the time. But there’re a lot more places where inventing something new makes sense.

Ignorance is Strength

Naturally, a lot of people are going to get the trade-off wrong. They’ll reinvent something that already exists. Or they’ll gratuitously use a dangerous feature when a perfectly serviceable safe feature is easier and faster to use. That’s the consequence of giving people a choice, sometimes they choose wrong. Especially if they are young, especially if they don’t know any better.

But guess what? Not knowing any better is what made David create Rails. he didn’t know Ruby doesn’t work for web applications. He didn’t know it’s wrong to create a web framework when there are so many sitting on the shelf waiting for you to use them right out of the box.

When I look at some code like (1..100).inject(&:+), I see the thing in Ruby that made Rails possible. I see all of the good things—and all of the bad things too. In that way, it’s totemic. And in truth, I think you can really decide for yourself what you think of Ruby just from looking at that one snippet of code.

If it scares the bejeezus out of you, if you start thinking of rules to impose on your team (we will use no more than three of the following seven gems on any one project. You will have an Architect approve any modification to one of the following 132 classes…), then Ruby is not for you.

But if you love (1..100).inject(&:+), if it fires up your imagination, if you think that you might write your own gem one day and see if the community likes your idea for improving the language, then I think you will be able to handle the inevitable snafus with a pragmatic shrug of the shoulders (Maybe I shouldn’t have extensively patched the Array class just so that I could steal the { x => y } notation for my pattern-matching DSL). You will evolve your judgement so that you know when to pull out the dangerous features and when to use restraint.

And whether you love open classes or hate them, whether you can think of a way to use them or whether you think there is always a better way, the indisputable truth about them is that Rubyists are using them to evolve the language from the bottom up, to find new ways to do things. Good things, bad things, beautiful things, ugly things… they are all New Things.

And ultimately, that is what this line of code says to me about Ruby. It says that this is a language where the fringe is inventing new things. And to embrace ruby is to embrace the idea of a language being propelled by its user base.

I use the language, so I obviously have some degree of comfort with the idea of a language evolving from the bottom up. But what do you think? It’s your opinion that really matters, not mine.


  1. Explanation: (1..100) creates a Range. For our purposes, this is equivalent to a collection of the whole numbers from one to one hundred, inclusive (The major way in which it differs from an array for us is that it doesn’t actually require one hundred spots in memory. Ranges are also useful for matching.) The #inject method is called fold or reduce in other languages. Expressed like this, it applies the “+” function to the elements of our range, giving us the sum. Primary school students could tell you an easier way to obtain the sum, of course, but we will ignore that for now.

    Ruby doesn’t actually have a function called +. What actually happens is that #inject wants a block. The & operator converts Proc objects into blocks and block into Proc objects. In this case, it tries to convert the symbol :+ into a block. The conversion uses Ruby’s built-in coercion mechanism. That mechanism checks to see whether we have a Proc object. If not, it sends the #to_proc method to the argument to make a Proc. If the Symbol :+ has a #to_proc method, it will be called. In Ruby 1.9 it has a #to_proc method. That method returns a Proc that takes its first argument and sends the + method to it along with any other arguments that may be present.

    So, &:+ really means { |x, y| x + y }. And the whole thing gives us a simple sum, just as (1..100).inject(&:*) would give us the product. Now that you know that, what does (1..100).map(&:to_s).map(&:size) do?

    [back]
  2. My favourite? Really?

    Actually, yes. Not the most powerful. Not the most compelling. Not even the most concise, and I don’t defend it as being superior to (1..100).inject { |acc, n| acc + n } in any way. And not my favourite line of code from any language: Ruby isn’t my favourite programming language, so it would be surprising if this were my all-time favourite line of code.

    But it is—to me—a totemic line of Ruby code. A line that demonstrates what a lot of Ruby is all about as a language. Its good and its bad. People have written to say that you coul duse sum or whatsis or jeebus to do the same thing in Ruby or in other languages, so this is a bad example. No it’s not! Trying to write an unassailable line of code that everyone will worship for its perfection is a fool’s errand. The point is to demonstrate something of Ruby’s character, especially in its evolution.

    As someone wisely pointed out, the equivalent code in other languages needn’t sum the numbers from one to one hundred, it needs to demonstrate something about that language’s culture and evolution. Java might show something with generics and type erasure. C# might do something with LINQ.

    [back]
 

Comments on “(1..100).inject(&:+):
Well, I guess the question is whether you want a code base which is in continual flux and may not do what you think it's doing, or if you want a codebase which is simple and stabilized.

Now, open classes are Teh Eevil. However, they are digging at a major pain point -- somehow, developers need the ability to easily extend functionality that's already in existence.

A much nicer approach, however, is offered by implied static typing. That lets you have your cake (the ability to add new functionality to existing codebase) and eat it, too (in the form of brevity and type safety).

Localized overloading is another potential solution which doesn't offer the potential damage of Ruby's classes.

Again, my standard caveat -- I'm a fan of open classes if my other option is Java's type system. However, it's not something that should be done willy-nilly, because it obscures the actual behavior of the code in a way that violates the Principle of Least Surprise. When I use && in my code, I expect it to mean the language-specific definition of &&. If you change that, and I have to come in and fix bugs in your code caused by that surprise, I will be cranky.
 
Robert:

You are right that there is a pain point, and I happen to agree with you that Open Classes are not the final word on how to address that pain point.

However, I think there is a more general issue here about the fact that a language which is predictable and stable is not evolving, regardless of how well it was designed to begin with.

We can argue about the exact mechanisms, but fundamentally I think that languages with dangerous features are the ones that offer the most potential for bottom-up evolution.

But this is like saying that Espresso is the best coffee... it isn't everybody's cup of tea ;-)
 
Is this not a false dichotomy, Reg?

How much experimentation and innovation you can tolerate in your language is going to be related to the context in which you use it.

Are you working with a small team with a lot of continuity, or a large one with lots of turnover? Are you building a one-off project or a monolithic, mission-critical app whose source code will haunt the organization for decades?

Depending on the use and circumstance, it may be obvious that one or the other (or something in between) is the better fit.

Choosing language features based on "marketing lifestyle", i.e. are you a young-at-heart innovator or a frightened stodgy-old-fart, probably isn't the best way to think about the choices.
 
Marc:

I have found that people's cultural biases drive their decision, and the technical factors come afterwards as they rationalize a decision they already made.

You say that the environment drives your tolerance for innovation. Hmmm... don't you think people choose whether to work for BigCo or for a startup for much the same reason they choose whether to work with tools that are mature and stable or are evolving rapidly?
 
Well, I'm probably just a brainwashed Pythonista, but I think I prefer the balance in the Python world.

In python-land, innovations happen from the community on up (witness: closures, generators, iterators, "with", any number more things) but they don't happen from people slinging around changes to fundamental python concepts in production code; they happen from considered debate followed finally by approval from above (Guido).

We have a language that's evolving enough, while still reliably stable inside each version.

(Not that I think it's the *best* solution, mind you. Just that I prefer that balance.)
 
Like I said, Bill, it's your opinion that matters!
 
I have found that people's cultural biases drive their decision

I agree this if often true, but I also think it is the wrong way to make decisions. ;-) I would argue that to be truly good at making this kind of technical choice, you need to deeply understand your problems and the range of options available to solve them. In a sense, an unthinking cultural bias to be an innovator by doing what the other innovators are doing is less creative and innovative than finding good trade-off solutions that fit the problems at hand.

don't you think people choose whether to work for BigCo or for a startup for much the same reason they choose whether to work with tools that are mature and stable or are evolving rapidly

Again you are assuming a mutually exclusive choice from which all else follows. Though BigCo is more likely to be conservative and the startup innovative in their technical choices, this is not necessarily always the case: BigCo could have an innovative department or product, while startup could have innovative marketing and sales strategy but conservative technology.

Surely you don't think that the only choice is to be one kind of cookie-cutter clone or the other?
 
Marc:

The race does not always go to the swift, nor the battle to the strong, but that is the way to bet.

We are all free to make our own decisions. I state that this is how a huge number of people make decisions about technology and tools. Should they? Perhaps not. Do they? Yes.

Should you choose Ruby becuase you like its culture? Should you choose Ruby because you enjoy an environment of change and evolution? I can't tell you what to do or what not to do, all I can do is point out the factors in play.

As I said, the article asks a question of its readers and attempts to explain the significance of the answer.

As for how to make choices, I am in favour of Bob Sutton's suggestion to have Strong Opinions, Weakly Held.
 
+1 to Bill.
 
It would seem that the majority of people still choose the Devil you can strongly type over the Devil you can't.
 
I love it - but the Smug Lisp^H^H^H^HForth Weenie in me kind of wonders what's so special about + that it can't be used in that context itself...
 
I like it. It's so Forth-ish.
 
However, I think there is a more general issue here about the fact that a language which is predictable and stable is not evolving, regardless of how well it was designed to begin with.

That's not true at all. Java is the most predictable and stable of popular languages, and even that is having a lot of evolving and exciting stuff going on (see Generics and the current Closure debate). Now, where the debate is coming from is from *outside* languages -- C++ in the case of Generics, and Groovy in the case of Closures. It maintains stability by adding new features at major release boundaries (Java2, Java5, Java7), so if you know what JVM version you're dealing with, you know what kind of code to expect.

The other thing to look at is languages like Ocaml, which have extremely stable core syntax, but which adds a syntax extension through the CamlP4 pre-processor. Again, when you're building, you have to explicitly handle the pre-processing, so you're not going to be surprised when some coder opens up your class and messes with it half-way through your code execution.
 
Robert:

I have been watching Java for ten years, and all I can say is that when you say the words "Having a lot of evolving and exciting stuff going on" you mean something different than when I say "Having a lot of evolving and exciting stuff going on."

Going through deep soul-searching over closures in 2008 is not what I call an exciting debate. But that's just me! As the post points out, it's your opinion that matters, not mine.
 
I would prefer a programming language that let one replace that line of code with the single token "5050".
 
"(1..100).inject(&:+)"

I don't like it, because it *should* be:

(1..100).inject(+)
 
Funny how you mention that snippet... it's amazingly close to my favorite one:

(1..10).reduce(&:*)

Key differences are that I much prefer "reduce" over "inject" (fortunately this alias is standard in 1.9) and I normally point to multiplication (many other languages have built-in "sum" methods, but few offer "factorial" or even "product" by default, so people tend to reply "so what, I just use array_sum in my favorite language and it is much more readable").

As for the range... I just tend to use much shorter values for demonstration :)
 
In the 1.4 to Java7 set of releases, Java has explored non-blocking I/O, generics, concurrency, non-blocking I/O, dynamic language support in the VM, new garbage collection methods, and now closures. And that's in the core language, not including library experimentation (like Spring).
 
The reason I don't like this is that I don't think it should be necessary to use to_proc. Why is + not a method? Just as I've always been able to do this:

['2007-01-01', '2008-01-01'].map &Date.method(:parse)

I want to do this:

(1..10).inject &method(:+)

Prefixing the last-argument with an ampersand has always worked for Proc and Method instances. Wrapping in a Proc seems like an indirect solution to the real problem.
 
&:+) is also looks like a really funny emoticon! Very nice post, Reg.
 
Nathan, Method objects generate Procs just like anything that is passed in &form to function calls. One would obviously expect that Method#to_proc generates a very lightweight wrapper, but I'd also expect Ruby 1.9's native Symbol#to_proc to be very efficient (I haven't checked).

So, in the end your proposed &method(:+) and &:+ would actually do the same thing, except &:+ is shorter and works in the language right now (since, by the current rules, method(:+) is akin to self.method(:+), not some "unbound_method_defer(:+)") :)
 
This post has been removed by the author.
 
Personally, I find

foldl1 (+) [1..100]

much cleaner and easier to use :-)
 
Pierre:

So do I, but this post is about why a certain piece of Ruby code exemplifies Ruby's innovate-from-the-fringe culture.

So... if what you quoted is Haskell, I like it but I can't "connect the dots" between it and this post.

On the other hand, if you show me a way to write that in Ruby and popularize it to the point where it is added to a future set of core libraries... it may become an even better example of innovation from the fringe.
 
My problem -- cause I always have at least one :-) -- is that finding this line of code for the very first time in my life, at 4am while trying to fix someone else's program, would cause me to throw something heavy across the room in frustration.

I love this type of thing when I am playing with a language, but it is just not 'industrial' enough for me to want to stick it into production. Cool is only cool if it isn't making you miserable.


Paul.
http://theprogrammersparadox.blogspot.com
 
Paul:

Remember we are talking about an evolving language. If you learned Ruby from 1.9, you would not need to throw the book across the room, this would be standard behaviour built into the core classes.

In Ruby 1.8, it is very popular but not built in. Therefore, you throw books until you learn the idiom, and thereafter you know it.

The point being that when people invent things like this, we get to do a little market testing: if everyone throws books, the ideas go away. if enough people like them, they make their way into the language.

That's why I picked this example instead of something like andand: andand has not been proven in the marketplace. It has not been accepted into the next version.
 
Hi Reg,

It is a great example, because it brings together my two opposing sides: a) I want to find more powerful ways to represent larger and larger chunks of functionality and b) I want a simple stable platform on which to build sophisticated systems.

In a way, when I put together some collection of primitive functions (in any language or paradigm) I am -- by virtue of my programming -- actually extending the functionality of the language. I can create a set of reusable building blocks that I can then apply to my specific problems. Most languages allow that to be built on top of them to some degree or another.

Something worries me about allowing everyone to go in low and extend the language. Perhaps it is just that too much flexibility is as dangerous as not enough? If the language is completely different at every shop, then is it really the same language?


Paul.
 
Your comments seem to push you in the direction of wanting a "central planning" language. It doesn't have to be Java, of course, I am told that Python works the same way.
 
The number of readers here, and in particular on N.YC, who seem unable to get past the literal syntax-as-solution of (1..100).inject(&:+) and grasp that the interesting bit isn't the literal utility of that line of code, or that there are others ways to implement the same functionality, or that pet language X has an easier/smaller/cleaner mechanism for summing integers, or that there are "better" lines of "cooler" code, is really quite depressing to me.

Is it really so difficult to work out that the interesting bits are in the mere existence - or not - of that code at all, per the developer's whim?

It's not my favourite piece of Ruby code. At the moment my favourite piece of Ruby code is:

module Kernel
private
def this_method()
caller[0] =~ /`([^']*)'/ and $1 + "()"
end
end

It's not hugely useful. You'll probably never use it. It won't win any awards and I hazard to guess if you don't know Ruby you won't likely tease out its purpose. And you can probably come up with a way to do the same in your favourite language. But it still makes me laugh every time I read it simply because it can exist at all.
 
Hi Reg,

Yes, but I don't think there are any current languages that would really make me happy. Oddly, languages like Java, VB and Pascal drive me nuts because they seem too rigid, while ones like Perl or C++ offer too much rope. No doubt they are all very different tools meant to solve different problems.

I'd like something that easily allows me to express the solution in the same way I talk about it with the users. I'd like it to have a consistent philosophy, and be extendable at the low levels. But at the high level, I want the language itself to enforce very rigid rules. If I assemble some blocks for other people, I want to be able to have the computer control how they are used. I want the rope, but I don't want everyone to have access to it.

Oh yea, I'd like a mansion and a yacht too, if possible. :-)


Paul.
 
Andrew said:
I would prefer a programming language that let one replace that line of code with the single token "5050".


There you go:


: 5050 DEPTH LOCALS| D |
  100 1 1- DO I LOOP
  BEGIN
    DEPTH D <>
  WHILE
    +
  REPEAT
;


It's uglier than it has to be, but I tried not to assume what "+" does, because, really, I can't. :-)

In fact, I discarded my first version after careful consideration of what "+" could do. :-)
 
There, I just click on the Publish link to realize a problem. The Ruby code is object oriented. The Forth code isn't, because there's no standard OO for Forth I could build upon (there are as many non-standard OO implementations as there are Forth programmers interested in OO :).

So I assumed "+" would take two numbers as parameters and return one number. The equivalent, really, of assuming the method + takes an object of the same class as self's class, and returns an object of self's class.

After all, you really can't assume "1" or "100" are numbers, can you? :-)
 
@mernen:
My explanation was pretty dodgy, but what I should have said instead of to_proc was Symbol#to_proc. Why is method calling delegated to the Symbol class? Should this be necessary when we have both Method and UnboundMethod?
 
I don't disagree with anything you've said in this post.

I don't know if this post is in response to my post about monkey patching, but you referenced it in the Reddit discussion, so I thought I'd respond. I think a lot of the people who read my post (or who stopped reading at at the tongue-in-cheek title) got the idea that I'm some sort of anti-dynamic troglodyte who is afraid of the very features that make Ruby powerful. All I can say to that is this: I have contributed monkey-patches to an open-source project. Recently. And I'm pretty pleased with what I was able to accomplish in that code. Yay for Ruby!

As a result I'm afraid a lot of people have missed the actual meat of my argument: that dynamic extension of classes is currently overused in Ruby, in ways that are:


* needless - another technique (such as a mixin, or locally extending individual objects) would have worked as well or better

* Overcomplicated - the use of a monkey patch actually created more work for the author

* fragile - the solution is tightly bound to third-party internals, reducing the usefulness of the plugin or gem because it is prone to breakage

* Excessively wide in scope - by hardcoding extensions to core classes, the author takes the choice to scope the change out of the plugin/gem user's hands, further limiting utility.

My point is that there are alternatives - often alternatives which are actually easier to implement and will make your plugin or gem more useful to the user. I don't see that there's anything wrong with trying to get this message out. I also don't think that there is necessarily tension between a) embracing powerful, dynamic features and b) evolving and promulgating good practices and guidelines for using those features.
 
Avdi:

This post was not a response to your post. For one thing, you used an expression, "M---- P----" that I think of as referring to changing an existing method, such as what happens when you rename a method, then stick your own replacement in that does some pre-processing before calling the original method.

When you do that kind of thing to core classes or to framework classes (as happens with some Rails plug-ins), you get a completely different kind of result than when you expand core classes with things like Symbol#to_proc.

So I really don't see the two posts as being seriously comparable.

In my case, I am not even interested in whether Symbol#to_proc is a good idea or whether, on the whole, giving people open classes is a god idea. I was just commenting that it has this interesting effect of growing a language from the fringe rather than (or perhaps in addition to) the core.
 
@nathan, mernen:

AFAICT &method(:+) gets the BoundMethod of the callling object, which is, almost certainly, not the method you want. Writing (1..100).inject(&Fixnum.instance_method(:+)) is both unwieldy and, in general wrong because it throws away the polymorphic aspect of &:+.

In this specific case, it's not an issue, but suppose the range were big enough that the numbers flipped over to Bignums? You can't use Integer.instance_method(:+) to get round it, because Integer doesn't actually have an implementation of +.

The robust ways of doing the job are: an explicit block (boring, repetitious); altering the implementation of map/inject/each to take a symbol as an argument (repetitious, special casey, ick); or implementing Symbol#to_proc somewhere (just right).
 
Running Ruby 1.8.7 now and the ampersand seems no longer to be needed:
>> (1..100).inject(:+)
=> 5050

This also works:
>> (100>>1)*100+(100>>1)
=> 5050

(yes, that was kind of a joke)
 
So, I'm late to this party, but here are my reactions:

(1) This counts as a change to the language, per se? I wouldn't have anticipated that.

(2) Looks like lots of powerful forms have yet to be implemented in Ruby.

I'm new enough to Ruby that I'm not sure I can say much more than that without wandering onto some other topic.
 




<< Home
Reg Braithwaite


Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

Books
What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

Share
rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

Beauty
IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

Work
The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Management
Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

Notation
The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Opinion
Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

Whimsey
The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

History
06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /