Ex-Perl coders: Howz it feel to convert to Ruby?

#!/usr/bin/perl

use strict;
use warnings;

package Foo;

sub new {
  my $class = shift;
  my %object = ( bar => "default",
           boat => "values",
           @_ );
  
  return bless \%object, $class;
}

sub bar { return shift->{bar}; }

sub boat { return shift->{boat}; }

sub concat {
  my $self = shift;
  
  return $self->{bar} . $self->{boat};
}

package main;

my $foo = Foo->new(bar => "bar", boat => "boat");
print $foo->concat, "\n";
print $foo->bar, "\n";
print $foo->boat, "\n";

# minor example of code difference
$foo->{bar} = "Not Private!";
print "\n", $foo->bar, "\n";

__END__

Hope that helps.

James Edward Gray II

···

On Sep 16, 2004, at 12:34 AM, Michael Geary wrote:

I don't need convincing, but I'd be interested in seeing the equivalent Perl
code just for the sake of comparison. I find it educational to see how
different languages approach a problem.

Hi --

···

On Fri, 17 Sep 2004, Phil Tomson wrote:

In article <Pine.LNX.4.44.0409160342510.17156-100000@wobblini>,
David A. Black <dblack@wobblini.net> wrote:
>Hi --
>
>On Thu, 16 Sep 2004, ChrisO wrote:
>>
>> Yes, this is a better example of what I was talking about, and yes, you
>> can provide better indication here in Ruby than in Perl, I have to
>> admit. So, to see the parallels, I'll bet you see a lot more of the
>> simple yields in block handling method definitions in classes than you
>> do in the form of you second example with the spiked out parameter and
>> the param.call approach. It's disciplined, well constructed code, but
>> not everyone is like that. Hence the Perl dilemma/diatribe here.
>
>There's nothing wrong with doing this the way Phil has, but there's
>also nothing wrong or undisciplined about using yield. Let's not
>stigmatize people who use yield (for example, Matz and many of the
>other authors of the Ruby standard library) as undisciplined. It's
>not a test of character; it's a programming idiom. You can work
>around it if you don't like it, as Phil has shown, or you can use it,
>as others have shown, in the course of writing perfectly good and
>clear code.

>
>When you supply a block to a method, you need to know how the block is
>going to be called -- which means you'll probably either have written
>the method yourself, or consulted some documentation that explains how
>the method works. So in practical terms, the use of yield vs. calling
>the block explicitly probably doesn't matter too much too often. You
>can use whichever semantics you like.
>
>

I agree totally. I did not intend to stigmatize people for using 'yield'.
In fact, I tend to use 'yield' as often as the alternative. I think I
sometimes prefer yield because even though it tends to make the arguments
to the method somewhat ambiguous, the intent can sometimes seem clearer in
the body of the method (vs the 'call').

I was referring mainly to Chris's characterization of yield users as
different from people who write disciplined, well-constructed code. I
suspect that Chris didn't really mean it as a slam to yield users
either, but it kind of read that way. (And I'm at least a frequent
yield user myself :slight_smile:

David

--
David A. Black
dblack@wobblini.net

[snip]

I actually wrote it anyway right after I posted the message above, just
to see what the difference would be:

Alternatively (tmtowtdi and all that ...):

  #!/usr/bin/perl -w
  use strict;

  my $foo = Foo->new('the');
  print $foo->concat, "\n";
  $foo->bar = 'Das';
  $foo->boat = 'Boot';
  print $foo->concat, "\n";

  {
    package Foo;
    my(%bar, %boat);
    sub new {
        my $self = bless {}, shift;
        $self->$_ = shift || $_ for ('bar','boat');
        $self;
    }
    sub bar : lvalue { $bar {$_[0]}}
    sub boat : lvalue { $boat {$_[0]}}
    sub concat { $_[0]->bar . $_[0]->boat }
  }
  __END__

regards,
andrew

···

On Thu, 16 Sep 2004 13:32:12 GMT, Chris <ceo@nospan.on.net> wrote:

And you can be "disciplined" by Chris' criteria while using yield:

def foo(arg, &block)
  yield
end

···

On Fri, Sep 17, 2004 at 03:20:05AM +0900, David A. Black wrote:

I was referring mainly to Chris's characterization of yield users as
different from people who write disciplined, well-constructed code. I
suspect that Chris didn't really mean it as a slam to yield users
either, but it kind of read that way. (And I'm at least a frequent
yield user myself :slight_smile:

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

David A. Black wrote:

Hi --

In article <Pine.LNX.4.44.0409160342510.17156-100000@wobblini>,

Hi --

Yes, this is a better example of what I was talking about, and yes, you can provide better indication here in Ruby than in Perl, I have to admit. So, to see the parallels, I'll bet you see a lot more of the simple yields in block handling method definitions in classes than you do in the form of you second example with the spiked out parameter and the param.call approach. It's disciplined, well constructed code, but not everyone is like that. Hence the Perl dilemma/diatribe here.

There's nothing wrong with doing this the way Phil has, but there's
also nothing wrong or undisciplined about using yield. Let's not
stigmatize people who use yield (for example, Matz and many of the
other authors of the Ruby standard library) as undisciplined. It's
not a test of character; it's a programming idiom. You can work
around it if you don't like it, as Phil has shown, or you can use it,
as others have shown, in the course of writing perfectly good and
clear code.

When you supply a block to a method, you need to know how the block is
going to be called -- which means you'll probably either have written
the method yourself, or consulted some documentation that explains how
the method works. So in practical terms, the use of yield vs. calling
the block explicitly probably doesn't matter too much too often. You
can use whichever semantics you like.

I agree totally. I did not intend to stigmatize people for using 'yield'.
In fact, I tend to use 'yield' as often as the alternative. I think I sometimes prefer yield because even though it tends to make the arguments to the method somewhat ambiguous, the intent can sometimes seem clearer in the body of the method (vs the 'call').

I was referring mainly to Chris's characterization of yield users as
different from people who write disciplined, well-constructed code. I
suspect that Chris didn't really mean it as a slam to yield users
either, but it kind of read that way. (And I'm at least a frequent
yield user myself :slight_smile:

Yeah, my rhetoric kinda made it sound that way. I'm not in a position yet to critique Ruby coding technique. But I do see a lot of undisciplined use of Perl syntax, and that was my main point. I just carried it over a little too far. Phil's point that the foo.call method *may* make things stand out a little better is still a noteworthy point however. But I don't think he was saying 'yield' was undisciplined coding.

-ceo

···

On Fri, 17 Sep 2004, Phil Tomson wrote:

David A. Black <dblack@wobblini.net> wrote:

On Thu, 16 Sep 2004, ChrisO wrote:

Andrew Johnson wrote:

···

On Thu, 16 Sep 2004 13:32:12 GMT, Chris <ceo@nospan.on.net> wrote:
[snip]

I actually wrote it anyway right after I posted the message above, just to see what the difference would be:

Alternatively (tmtowtdi and all that ...):

  #!/usr/bin/perl -w
  use strict;

  my $foo = Foo->new('the');
  print $foo->concat, "\n";
  $foo->bar = 'Das';
  $foo->boat = 'Boot';
  print $foo->concat, "\n";

  { package Foo;
    my(%bar, %boat);
    sub new {
        my $self = bless {}, shift;
        $self->$_ = shift || $_ for ('bar','boat');
        $self;
    }
    sub bar : lvalue { $bar {$_[0]}}
    sub boat : lvalue { $boat {$_[0]}}
    sub concat { $_[0]->bar . $_[0]->boat }
  }
  __END__

regards,
andrew

Oooooo... Aaaaaah...! lvalues... You don't see folks using them very much. I don't use them much myself.

-ceo

Mauricio Fernández wrote:

I was referring mainly to Chris's characterization of yield users as
different from people who write disciplined, well-constructed code. I
suspect that Chris didn't really mean it as a slam to yield users
either, but it kind of read that way. (And I'm at least a frequent
yield user myself :slight_smile:

And you can be "disciplined" by Chris' criteria while using yield:

def foo(arg, &block)
  yield
end

...although this does have a small speed penalty as a Proc object gets created by the &block construct, IIRC.

Why not just use the rdoc markup as a convention, if you want to be explicit?

def foo(arg) # :yields: some_useful_object
   yield foo(arg)
end

···

On Fri, Sep 17, 2004 at 03:20:05AM +0900, David A. Black wrote:

Hello all,

Is there any way to change an outside-scoped object inside a proc? Such
as:

a = "Foo"
b = "bar"
proc = Proc.new { |val1, val2| val1, val2 = "boo", "baz" }
proc.call( a, b )

# a and b are still "Foo" and "bar"

I don't believe this is possible.. but would be very nice for creating a
collect! method for a user-defined object. Thanks

Nate

I suspect you are right (that it isn't possible), at least without
going through some rather extreme contortions. Over all, I'm _glad_
that this is the case (though I must admit I've past parameters in "var"
with wild abandon in other languages).

     Why do you think this is needed (or nice) for writing a collect!
method? Why cant you just write something like:

    class My_collection_class
        def (k)
            :
            end
        def =(k,v)
            :
            end
        def each_key
            :
            end
        def collect!
            each_key { |k| self[k] = yield(self[k]) }
            end
        end

-- MarkusQ

···

On Thu, 2004-09-16 at 10:28, Nate Smith wrote:

Is there any way to change an outside-scoped object inside a proc? Such
as:

a = "Foo"
b = "bar"
proc = Proc.new { |val1, val2| val1, val2 = "boo", "baz" }
proc.call( a, b )

# a and b are still "Foo" and "bar"

I don't believe this is possible.. but would be very nice for creating a
collect! method for a user-defined object. Thanks

Nate,

You have to use mutable objects. Variables in Ruby, whether in blocks
or elsewhere, are references, not pointers. If you reassign a
variable, you're just changing which object it refers to, rather than
updating the value of a memory location.

Luckily, Ruby strings are mutable, so at least for your example, you
could do the following:

a = "Foo"
b = "Bar"
myproc = Proc.new {|val1, val2| # note: I try to avoid shadowing a
built-in like 'proc'
  val1[1..-1] = "boo" # with a variable name
  val2[1..-1] = "baz"
}
proc.call(a, b)

It might be interesting to define something like Common Lisp's 'setf'
function, which is a sort of generic setter. So, mutable objects like
strings, arrays, hashes, etc., could all support a 'value=' or
'replace' method which would make them destructively update their
value.

···

--
Lennon
rcoder.net

Is there any way to change an outside-scoped object inside a proc? Such
as:

a = "Foo"
b = "bar"
proc = Proc.new { |val1, val2| val1, val2 = "boo", "baz" }
proc.call( a, b )

# a and b are still "Foo" and "bar"

I don't believe this is possible.. but would be very nice for creating a
collect! method for a user-defined object. Thanks

a, b = "foo", "bar"
proc = Proc.new {|a, b|}
proc.call(1, 2)

# a, b => 1, 2

It might be interesting to define something like Common Lisp's 'setf'
function, which is a sort of generic setter. So, mutable objects like
strings, arrays, hashes, etc., could all support a 'value=' or
'replace' method which would make them destructively update their
value.

     I played around with adding a method Module#become(other) (IIRC)
that would cause an object to replace itself with another; the intended
use was a distribution/persistence layer that could invisibly swap
objects out and replace them with stubs that would then bring back the
original object on access.

     Again, if I recall correctly, I got it to the sort-of-working
stage, but bogged down in dealing with the details of some of the
built-in classes, singletons, etc.

-- MarkusQ

Very true indeed (that works great). I was more thinking about
collecting for the key and the value in one swoop, but that may be more
trouble than it's worth.

Nate

···

On Thu, 2004-09-16 at 14:36, Markus wrote:

On Thu, 2004-09-16 at 10:28, Nate Smith wrote:

> Is there any way to change an outside-scoped object inside a proc? Such
> as:
> ........

     Why do you think this is needed (or nice) for writing a collect!
method? Why cant you just write something like:

    class My_collection_class
        def (k)
            :
            end
        def =(k,v)
            :
            end
        def each_key
            :
            end
        def collect!
            each_key { |k| self[k] = yield(self[k]) }
            end
        end

This only works as long as the variables and block params have the
same name. If you want to be able to pass the variables to be updated
to proc, as the original poster requested, then it won't work.

It is a great way to really confuse the hell out of someone reading
your code, though; the variable/parameter scoping issues surrounding
blocks are one of the few things I really consider to be a "wart" in
the core Ruby language.

···

--
Lennon
rcoder.net

Actually, I think I just described the 'replace' method, which String,
Array, and Hash all implement...I really should just assume that the
standard library already does anything clever I can think of.

···

--
Lennon
rcoder.net