Syntactic sugar idea


#1

It seems that often an object will be passed into a block only to invoke
a method of that object:
arr.map{ |obj| obj.some_method }

So I had the (weird? stupid?) thought that it would be nice to have some
syntactic sugar like this:
arr.map{ .some_method }

Does that make any sense?


#2

On Wed, May 13, 2009 at 10:42 PM, Daniel DeLorme removed_email_address@domain.invalid
wrote:

It seems that often an object will be passed into a block only to invoke
a method of that object:
arr.map{ |obj| obj.some_method }

So I had the (weird? stupid?) thought that it would be nice to have some
syntactic sugar like this:
arr.map{ .some_method }

Does that make any sense?

RUBY_VERSION
=> “1.9.1”

%w[foo bar baz].map(&:upcase)
=> [“FOO”, “BAR”, “BAZ”]

On Ruby 1.8.6, you can implement this easily as:

class Symbol
def to_proc
lambda { |x| x.send(self)
end
end

-greg


#3

Jan wrote:

You can do arr.map(&:some_method) in ruby 1.9
Yes, I know, and that was kind of my point. The to_proc conversion
became popular because people want a shortcut notation for this common
case, but IMHO map(&:foo) is way uglier than map{.foo}, and as a bonus
you could even do map{.foo.bar} :slight_smile:

-Daniel


#4

It seems that often an object will be passed into a block only to invoke
a method of that object:
arr.map{ |obj| obj.some_method }

So I had the (weird? stupid?) thought that it would be nice to have some
syntactic sugar like this:
arr.map{ .some_method }

You can do arr.map(&:some_method) in ruby 1.9

-J


#5

On Wed, May 13, 2009 at 11:03 PM, Daniel DeLorme removed_email_address@domain.invalid
wrote:

Yes, I know, and that was kind of my point. The to_proc conversion
became popular because people want a shortcut notation for this common
case, but IMHO map(&:foo) is way uglier than map{.foo}, and as a bonus you
could even do map{.foo.bar} :slight_smile:

I don’t know. I think they’re about the same in terms of looks, and
the former doesn’t require changes to the parser.

-greg


#6

On May 13, 2009, at 11:07 PM, Gregory B. wrote:

the former doesn’t require changes to the parser.

-greg

plus, doing your .foo.bar has to contend with .foo being nil (or
otherwise having a NoMethodError for :bar)

some_collection.map{|e|e.foo.bar}

really isn’t too bad anyway and if you want to handle the possible
exception:

some_collection.map{|e|e.foo.bar rescue nil}

possibly with a .compact thrown on the end.

The parser already refused to treat .5 as a Float literal insisting
that it be 0.5 so I doubt that .foo would get any more favorable
treatment as a special case (since .5 isn’t really all that special).

-Rob

Rob B. http://agileconsultingllc.com
removed_email_address@domain.invalid


#7

Rob B. wrote:

The parser already refused to treat .5 as a Float literal insisting that
it be 0.5 so I doubt that .foo would get any more favorable treatment as
a special case (since .5 isn’t really all that special).

Well, you never know… Matz did add the Symbol#to_proc conversion in
1.9 and also the foo\n.bar “fluent interface” syntax. So apparently
trivial changes do make their way into core… sometimes.

-Daniel


#8

Daniel DeLorme wrote:

It seems that often an object will be passed into a block only to invoke
a method of that object:
arr.map{ |obj| obj.some_method }

So I had the (weird? stupid?) thought that it would be nice to have some
syntactic sugar like this:
arr.map{ .some_method }

Does that make any sense?

It would be nice for avoiding instance_eval in DSLs:

@x = 400
@y = 300

config “my window” do
.width @x
.height @y
end

but I just don’t see how to fit it into ruby…


#9

On May 14, 2009, at 12:27 AM, Joel VanderWerf wrote:

but I just don’t see how to fit it into ruby…
Isn’t this essentially the “with” syntax from Javascript? I think that
could fit into a future version of Ruby…

  • Josh

#10

On Thu, May 14, 2009 at 12:16 AM, Daniel DeLorme removed_email_address@domain.invalid
wrote:

Rob B. wrote:

The parser already refused to treat .5 as a Float literal insisting that
it be 0.5 so I doubt that .foo would get any more favorable treatment as a
special case (since .5 isn’t really all that special).

Well, you never know… Matz did add the Symbol#to_proc conversion in
1.9 and also the foo\n.bar “fluent interface” syntax. So apparently
trivial changes do make their way into core… sometimes.

But Symbol#to_proc is not a parser change. It’s just using an existing
hook that has been around in Ruby 1.8

-greg


#11

2009/5/14 Daniel DeLorme removed_email_address@domain.invalid:

But the fluent interface change is a parser change. My point was just that
seemingly trivial requests can make it into the core, whether they’re a
syntax change or not.

We always need to balance cost and benefit. In this case to me the
benefit seems to be outweighed by costs whereas the other change can
have an impact on readability. YMMV though.

Cheers

robert


#12

Gregory B. wrote:

On Thu, May 14, 2009 at 12:16 AM, Daniel DeLorme removed_email_address@domain.invalid wrote:

Well, you never know… Matz did add the Symbol#to_proc conversion in
1.9 and also the foo\n.bar “fluent interface” syntax. So apparently
trivial changes do make their way into core… sometimes.

But Symbol#to_proc is not a parser change. It’s just using an existing
hook that has been around in Ruby 1.8

But the fluent interface change is a parser change. My point was just
that seemingly trivial requests can make it into the core, whether
they’re a syntax change or not.

-Daniel


#13

On Thu, May 14, 2009 at 4:42 AM, Daniel DeLorme removed_email_address@domain.invalid
wrote:

It seems that often an object will be passed into a block only to invoke
a method of that object:
arr.map{ |obj| obj.some_method }

So I had the (weird? stupid?) thought that it would be nice to have some
syntactic sugar like this:
arr.map{ .some_method }

I sometimes miss Perl’s $_. Without a parser change, it could be used
as the default name for block parameters:

arr.sort_by { $.size }
File.open(“logfile”, “a”) { $
.puts logtext }
arr.map { foo2bar($_) }

Then I remember how people abuse it and avoid using appropriately
named variables, and I am again happy about the little extra code I
have to write.


#14

From: “Lars C.” removed_email_address@domain.invalid

I sometimes miss Perl’s $_.

Not that I’m advocating its use, but… Ruby does support $_
for perl-like command line scripting:

$ ruby -ne ‘puts “dollar_underscore is: #$_”’ /usr/share/dict/words |
head
dollar_underscore is:
dollar_underscore is: A
dollar_underscore is: A’s
dollar_underscore is: AOL
dollar_underscore is: AOL’s
dollar_underscore is: Aachen
dollar_underscore is: Aachen’s
dollar_underscore is: Aaliyah
dollar_underscore is: Aaliyah’s
dollar_underscore is: Aaron

Regards,

Bill


#15

Robert K. wrote:

We always need to balance cost and benefit. In this case to me the
benefit seems to be outweighed by costs

Right, what folk might not realise is that the 1.8 version constructed
an object on each usage which had to be GC’d later, whereas in the 1.9
version it can be free. So there was good reason to support it directly.

Clifford H…


#16

This is a neat idea, but wouldn’t it conflict with the “fluent
interface” construct? Using Joel’s DSL example:

config “my window” do
.width @x
.height @y
end

if we changed it to

config “my window” do
.width calculate_width
.height @y
end

Now how does the parser know what .height refers to: calculate_width,
or the implicit block variable?


#17

Robert K. wrote:

We always need to balance cost and benefit. In this case to me the
benefit seems to be outweighed by costs whereas the other change can
have an impact on readability. YMMV though.

The cost isn’t high. 15 minutes of work in JRuby, probably more in CRuby
but not by a lot.

  • Charlie

#18

2009/5/14 Charles Oliver N. removed_email_address@domain.invalid

Robert K. wrote:

We always need to balance cost and benefit. In this case to me the
benefit seems to be outweighed by costs whereas the other change can
have an impact on readability. YMMV though.

The cost isn’t high. 15 minutes of work in JRuby, probably more in CRuby but not by a lot.

That’s likely only implementation. Then there is testing,
documentation and before that checking that there are no negative
effects of the change. If there are - and there seems to be evidence
that this is the case in Mark’s posting - those negative effects also
count as costs…

Kind regards

robert


#19

Daniel DeLorme wrote:

It seems that often an object will be passed into a block only to invoke
a method of that object:
arr.map{ |obj| obj.some_method }

So I had the (weird? stupid?) thought that it would be nice to have some
syntactic sugar like this:
arr.map{ .some_method }

Does that make any sense?

Groovy has this in the form of the “it” magic variable:

[1,2,3].each {puts it}

While I absolutely hate the moniker “it” the idea itself has grown on
me. Perhaps something more scala-like:

[1,2,3].each {puts _}

Or a pseudo-global:

[1,2,3].each {puts $it}

I hacked “it” to work in JRuby once recently, and it’s not difficult.

  • Charlie

#20

Robert K. wrote:

That’s likely only implementation. Then there is testing,
documentation and before that checking that there are no negative
effects of the change. If there are - and there seems to be evidence
that this is the case in Mark’s posting - those negative effects also
count as costs…

Bah, I say.

~/projects/jruby âž” jruby -X-C -e ‘[1,2,3].each {puts $it}’
1
2
3

Diff follows.

diff --git a/src/org/jruby/RubyGlobal.java
b/src/org/jruby/RubyGlobal.java
index f25bd42…c107a7b 100644
— a/src/org/jruby/RubyGlobal.java
+++ b/src/org/jruby/RubyGlobal.java
@@ -199,6 +199,7 @@ public class RubyGlobal {

      runtime.defineVariable(new ErrorInfoGlobalVariable(runtime,

“$!”, runtime.getNil()));
runtime.defineVariable(new NonEffectiveGlobalVariable(runtime,
“$=”, runtime.getFalse()));

  •    runtime.defineVariable(new ImplicitItGlobalVariable(runtime,
    

“$it”));

      if(runtime.getInstanceConfig().getInputFieldSeparator() == 

null) {
runtime.defineVariable(new GlobalVariable(runtime, “$;”,
runtime.getNil()));
@@ -320,6 +321,24 @@ public class RubyGlobal {
}
}

  • private static class ImplicitItGlobalVariable extends
    GlobalVariable {
  •    public ImplicitItGlobalVariable(Ruby runtime, String name) {
    
  •        super(runtime, name, null);
    
  •    }
    
  •    @Override
    
  •    public IRubyObject set(IRubyObject value) {
    
  •        return
    

runtime.getCurrentContext().getCurrentScope().setImplicitArg(value);

  •    }
    
  •    @Override
    
  •    public IRubyObject get() {
    
  •        IRubyObject obj =
    

runtime.getCurrentContext().getCurrentScope().getImplicitArg();

  •        if (obj == null) obj = runtime.getNil();
    
  •        return obj;
    
  •    }
    
  • }
  • private static class LastExitStatusVariable extends GlobalVariable 
    

{
public LastExitStatusVariable(Ruby runtime, String name) {
super(runtime, name, runtime.getNil());
diff --git a/src/org/jruby/runtime/DynamicScope.java
b/src/org/jruby/runtime/DynamicScope.java
index e2f6e90…9ad9deb 100644
— a/src/org/jruby/runtime/DynamicScope.java
+++ b/src/org/jruby/runtime/DynamicScope.java
@@ -39,6 +39,8 @@ public abstract class DynamicScope {
// been called.
protected DynamicScope evalScope;

  • protected IRubyObject implicitArg;
  • protected DynamicScope(StaticScope staticScope, DynamicScope 
    

parent) {
this.staticScope = staticScope;
this.parent = parent;
@@ -165,6 +167,14 @@ public abstract class DynamicScope {
return staticScope.getAllNamesInScope();
}

  • public IRubyObject getImplicitArg() {
  •    return implicitArg;
    
  • }
  • public IRubyObject setImplicitArg(IRubyObject implicitArg) {
  •    return this.implicitArg = implicitArg;
    
  • }
  • /**
     * Get backref
     */
    

diff --git a/src/org/jruby/runtime/InterpretedBlock.java
b/src/org/jruby/runtime/InterpretedBlock.java
index 8015be8…4291066 100644
— a/src/org/jruby/runtime/InterpretedBlock.java
+++ b/src/org/jruby/runtime/InterpretedBlock.java
@@ -162,6 +162,7 @@ public class InterpretedBlock extends BlockBody {
Frame lastFrame = pre(context, null, binding);

      try {
  •        context.getCurrentScope().setImplicitArg(value);
            if (hasVarNode) {
                setupBlockArg(context, varNode, value, self);
            }