Pushing Forward
Perl6 will include so called "piping" operators, something I'm looking
forward to. The follow example is copied out of the Perl6 Synopsis
and shows how you can rewrite this code:
@result = map { floor($^x / 2) },
grep { /^ \d+ $/ },
@data;
as:
@data ==> grep { /^\d+$/ }
==> map { floor($^x / 2) }
==> @result;
Wait, so why am I looking forward to more gibberish operators?
Well, for one, pipe operators let you write certain chained
operations from left to right (ie, postfix notation) which might
otherwise have needed to be written right to left. Now obviously,
postfix method invocation is pretty standard these days, so anytime
you are using methods in those languages (Perl included), you're
already going left to right. For example in Ruby:
result = data.grep(/^\d+$/).map{|x| (x.to_f/2).floor}
Of course, this doesn't put the assignment at the end like the Perl6
code does, but that's not a huge deal. You can very clearly tell that
grep happens first and map happens second.
Of course, postfix is not just magically always the right way to write
things. Take this example Smalltalk code from comp.lang.smalltalk.dolphin:
[:x| x sqrt ] value: ([:x| x + 5 ] value: ([:x| x * x ] value: 2))See how postfix ordering has given us something that still reads the wrong direction? Admittedly, the blocks are to blame here for reversing the reading direction, but Behavior objects can cause the same problem. Howard Oh suggests adding an arrow method to solve this problem in Smalltalk:
2 --> [:x| x * x ] --> [:x| x + 5 ] --> [:x| x sqrt ]Pretty cool. Even cooler, however, is the fact that such a method already exists in Squeak (named "in"). Blaine Buxton shows it off:
((2 in: [:x| x * x ]) in: [:x| x + 5 ]) in: [:x| x sqrt ]There's some grody parenthesization in there, but that's just part of the price you pay for Smalltalk's method syntax. Now, Ruby uses ".call()" instead of "value:", but you can get into the same situation with Ruby blocks. Well, actually, you probably won't. Blocks, which are syntactically concise, are only usable when invoking a method. In other situations you must use syntactically unweildy Proc objects. How unweildy? Well, check this out...
Proc.new{|x| Math.sqrt(x) }.call(Proc.new{|x| x + 5}.call(Proc.new{|x| x * x}.call(2)))
or
lambda{|x| Math.sqrt(x) }.call(lambda{|x| x + 5}.call(lambda{|x| x * x}.call(2)))
Yuck, right? Well, the good news is that the "in" syntax actually
makes this code something that you'd almost feel okay about writing.
class Object
def in
yield self
end
end
That gets you most of the way there and lets you write:
2.in{|x| x * x }.in{|x| x + 5 }.in{|x| Math.sqrt(x) }
Of course, the big win here is just that we got to use blocks instead
of Procs. But you could modify the definition of "in" further like
this:
class Object
def in(*args, &block)
(block || args[0]).call(self)
end
end
And write:
2.in(Proc.new{|x| x * x }).in(Proc.new{|x| x + 5 }).in(Proc.new{|x| Math.sqrt(x) })
But you'd be crazy.
posted on: 06/12/2006 | path: /tech