with

  • Thread starter Matthias Wächter
  • Start date
M

Matthias Wächter

Sorry folks if this was raised already, but google is not very
useful when asked for "ruby with" or "ruby map single object" ...


Is there something like "with" already available? Let me give the
short implementation first so the gurus know right away what I am after:

module Kernel
def with
yield self
end
end

What does it do? It can glue an object and a block together. Just
append ".with {|x| block}" to your object. What's that good for?

When I have an array (or range or similar) and I want to do
something with each member, I can already do that by simply adding
".map {|elem| do_something_with(elem)}". The result can be used
transparently within another calculation. #each, #map and #inject
are good candidates for something like that. So far, so good.

Take the following situation:
result= a_long_calculation_start ...
... a_very_long_object_calculation ...
... calculation_end

What can I do now if I have to apply a function on
"a_very_long_object_calculation" in the example above? Since
a_very_long_object_calculation is not an array, I cannot use
#each/#map/#inject. So let's put the function call around the
calculation:

Solution 1:
result= a_long_calculation_start ...
... function(a_very_long_object_calculation) ...
... calculation_end

But what if "function" is an in-place calculation containing of if's
and loops? Then I have to move all that away from the long object
calculation:

Solution 2:
intermediate= a_very_long_object_calculation
intermediate_result= function(intermediate)
result= a_long_calculation_start ...
... intermediate_result ...
... calculation_end

Obviously, while reaching the goal, the function is now ugly
disrupted. Even Solution 1 suffers from the fact that I have to at
least put some text into the expression before
a_very_long_object_calculation appears. What would be nice if I
could implement the in-place calculation inside of a block like with
#each, #map or #inject:
result= a_long_calculation_start ...
... a_very_long_object_calculation.with{|val| function(val)} ...
... calculation_end

With "with", the following proportions are solved:

1. I can put arbitrary functions into the block following #with.

2. I don't have to put additional characters before
a_very_long_object_calculation -- succeeding calculations are behind
the preceding object calculation.

Am I the first to ask for something like this or is this already
implemented in a way that I couldn't find it?

BTW: "with" could be renamed to something short and similar -- f.e.
smap (single map), so there could be a .seach (single each) as well
returning "self" after the yield. .sinject is not very useful.

module Kernel
def smap
yield self
end
def seach
yield self
self
end
end

Thanks,
- Matthias
 
R

Robert Klemme

Sorry folks if this was raised already, but google is not very
useful when asked for "ruby with" or "ruby map single object" ...


Is there something like "with" already available? Let me give the
short implementation first so the gurus know right away what I am after:

module Kernel
def with
yield self
end
end

What does it do? It can glue an object and a block together. Just
append ".with {|x| block}" to your object. What's that good for?

When I have an array (or range or similar) and I want to do
something with each member, I can already do that by simply adding
".map {|elem| do_something_with(elem)}". The result can be used
transparently within another calculation. #each, #map and #inject
are good candidates for something like that. So far, so good.

Take the following situation:


What can I do now if I have to apply a function on
"a_very_long_object_calculation" in the example above? Since
a_very_long_object_calculation is not an array, I cannot use
#each/#map/#inject. So let's put the function call around the
calculation:

Solution 1:


But what if "function" is an in-place calculation containing of if's
and loops? Then I have to move all that away from the long object
calculation:

Solution 2:


Obviously, while reaching the goal, the function is now ugly
disrupted. Even Solution 1 suffers from the fact that I have to at
least put some text into the expression before
a_very_long_object_calculation appears. What would be nice if I
could implement the in-place calculation inside of a block like with
#each, #map or #inject:


With "with", the following proportions are solved:

1. I can put arbitrary functions into the block following #with.

2. I don't have to put additional characters before
a_very_long_object_calculation -- succeeding calculations are behind
the preceding object calculation.

Am I the first to ask for something like this or is this already
implemented in a way that I couldn't find it?

BTW: "with" could be renamed to something short and similar -- f.e.
smap (single map), so there could be a .seach (single each) as well
returning "self" after the yield. .sinject is not very useful.

module Kernel
def smap
yield self
end
def seach
yield self
self
end
end

Thanks,
- Matthias

Somewhere above you completely lost me - it is especially unclear to me
what "a_very_long_calculation" is supposed to be. Is this a variable
name? Is this a placeholder for a long expression?

Anyway, I do wonder whether all this can be nicely solved by using local
variables. Basically #with does nothing else than binding a value to a
local variable with limited scope.

Kind regards

robert
 
M

Matthias Wächter

Robert said:
Somewhere above you completely lost me - it is especially unclear to me
what "a_very_long_calculation" is supposed to be. Is this a variable
name? Is this a placeholder for a long expression?

It's the latter.

Suppose you do a calculation like the following:

a=["abc","thing"].
map{|x| x.length}.
inject(0){|sum,x|sum+x}.
to_f**0.5

(this is only for the sake of example, don't try to make sense out of it).

When I want to insert a function between .map and .inject, I can simply do that:

a=["abc","thing"].
map{|x| x.length}.
map{|x| x>5 ? 5 : x}.
inject(0){|sum,x|sum+x}.
to_f**0.5

I don't have to change any part of the expression before or after my change, it's just an *insert*.

But I can't do the same for values that are not arrays. Say, after .to_f I want to set bounds on the value so that the square root function does not raise an exception. Then .with is useful.

a=["abc","thing"].
map{|x| x.length}.
map{|x| x>5 ? 5 : x}.
inject(0){|sum,x|sum+x}.
to_f.
with{|f| f<0.0 ? 0.0 : f}**0.5

Otherwise I'd have to split execution at this place, assign to a temporary variable, make my calculation and continue the expression later on.

temp=["abc","thing"].
map{|x| x.length}.
map{|x| x>5 ? 5 : x}.
inject(0){|sum,x|sum+x}.
to_f
if temp<0.0
temp = 0.0
end
a=temp**0.5

I like the 'with' version better.
Anyway, I do wonder whether all this can be nicely solved by using local
variables. Basically #with does nothing else than binding a value to a
local variable with limited scope.

Ruby tries to avoid local variables wherever it can by chaining iterators together like in file-open-each-line-gsub. I tried to allow a block to be used for non-enumerable types as well.

What .with does is producing a little space for writing a block instead of requiring to reorder the whole function just for adding this little piggy-bag change.

Does it make sense now?

- Matthias
 
S

Stefan Rusterholz

Matthias said:
Sorry folks if this was raised already, but google is not very
useful when asked for "ruby with" or "ruby map single object" ...


Is there something like "with" already available? Let me give the
short implementation first so the gurus know right away what I am after:

module Kernel
def with
yield self
end
end

What does it do? It can glue an object and a block together. Just
append ".with {|x| block}" to your object. What's that good for?

*snip* (ruby-forum.com doesn't like too much quoted text)

Thanks,
- Matthias

Sometimes less is more ;-)
I'm not sure I really undestood what you want, but maybe instance_eval
is what you want or at least comes close to it.

Regards
Stefan
 
W

William James

Matthias said:
Robert said:
Somewhere above you completely lost me - it is especially unclear to me
what "a_very_long_calculation" is supposed to be. Is this a variable
name? Is this a placeholder for a long expression?

It's the latter.

Suppose you do a calculation like the following:

a=["abc","thing"].
map{|x| x.length}.
inject(0){|sum,x|sum+x}.
to_f**0.5

(this is only for the sake of example, don't try to make sense out of it).

When I want to insert a function between .map and .inject, I can simply do that:

a=["abc","thing"].
map{|x| x.length}.
map{|x| x>5 ? 5 : x}.
inject(0){|sum,x|sum+x}.
to_f**0.5

I don't have to change any part of the expression before or after my change, it's just an *insert*.

But I can't do the same for values that are not arrays. Say, after .to_f I want to set bounds on the value so that the square root function does notraise an exception. Then .with is useful.

a=["abc","thing"].
map{|x| x.length}.
map{|x| x>5 ? 5 : x}.
inject(0){|sum,x|sum+x}.
to_f.
with{|f| f<0.0 ? 0.0 : f}**0.5

Otherwise I'd have to split execution at this place, assign to a temporary variable, make my calculation and continue the expression later on.

So "with" lets you keep the chain going.
This won't do that, but it avoids a temporary variable:

a =
[ ["abc","thing"].
map{|x| x.size }.
map{|x| x>5 ? 5 : x}.
inject{|sum,x| sum + x }.
to_f, 0.0 ].max ** 0.5

As suggested by someone else, "instance_eval" can
be used:

a =
["abc","thing"].
map{|x| x.size }.
map{|x| x>5 ? 5 : x}.
inject{|sum,x| sum + x }.
to_f.instance_eval{ [self, 0.0].max} ** 0.5
 
N

Nobuyoshi Nakada

Hi,

At Tue, 4 Sep 2007 00:15:32 +0900,
Matthias W=E4chter wrote in [ruby-talk:267357]:
Is there something like "with" already available? Let me give the
short implementation first so the gurus know right away what I am after:
=20
module Kernel
def with
yield self
end
end

"tap" in 1.9 is same.

--=20
Nobu Nakada
 
M

Michael Fellinger

T24gOS80LzA3LCBOb2J1eW9zaGkgTmFrYWRhIDxub2J1QHJ1YnktbGFuZy5vcmc+IHdyb3RlOgo+
IEhpLAo+Cj4gQXQgVHVlLCA0IFNlcCAyMDA3IDAwOjE1OjMyICswOTAwLAo+IE1hdHRoaWFzIFfD
pGNodGVyIHdyb3RlIGluIFtydWJ5LXRhbGs6MjY3MzU3XToKPiA+IElzIHRoZXJlIHNvbWV0aGlu
ZyBsaWtlICJ3aXRoIiBhbHJlYWR5IGF2YWlsYWJsZT8gTGV0IG1lIGdpdmUgdGhlCj4gPiBzaG9y
dCBpbXBsZW1lbnRhdGlvbiBmaXJzdCBzbyB0aGUgZ3VydXMga25vdyByaWdodCBhd2F5IHdoYXQg
SSBhbSBhZnRlcjoKPiA+Cj4gPiBtb2R1bGUgS2VybmVsCj4gPiAgIGRlZiB3aXRoCj4gPiAgICAg
eWllbGQgc2VsZgo+ID4gICBlbmQKPiA+IGVuZAo+Cj4gInRhcCIgaW4gMS45IGlzIHNhbWUuCgpU
aGV5IGhhdmUgZGlmZmVyZW50IGVmZmVjdHMsIHdoaWxlICd0YXAnIHdpbGwgcmV0dXJuIHNlbGYg
YW5kIHRocm93CmF3YXkgcmVzdWx0cyBvZiB0aGUgYmxvY2ssICd3aXRoJyByZXR1cm5zIHRoZSBy
ZXN1bHQgb2YgdGhlIGJsb2NrLgoKW21hbnZlcnVAcGkgfl0kIFJVQllPUFQ9IGlyYjE5CmNsYXNz
IE9iamVjdAogIGRlZiB3aXRoCiAgICB5aWVsZChzZWxmKQogIGVuZAplbmQKIyBuaWwKMS50YXB7
fGV8IGUgKyAxfSArIDIKIyAzCjEud2l0aHt8ZXwgZSArIDF9ICsgMgojIDQKCl4gbWFudmVydQo=
 
M

Matthias Wächter

Great to hear that this is really not available yet :) and I'm not
the only one thinking it should be!
They have different effects, while 'tap' will return self and throw
away results of the block, 'with' returns the result of the block.

[manveru@pi ~]$ RUBYOPT= irb19
class Object
def with
yield(self)
end
end
# nil
1.tap{|e| e + 1} + 2
# 3
1.with{|e| e + 1} + 2
# 4

So it's like .with == .single-map, .tap == .single-each

Right?

- Matthias
 
M

Michael Fellinger

T24gOS80LzA3LCBNYXR0aGlhcyBXw6RjaHRlciA8bWF0dGhpYXNAd2FlY2h0ZXIud2l6LmF0PiB3
cm90ZToKPiBPbiAwNC4wOS4yMDA3IDA5OjQ3LCBNaWNoYWVsIEZlbGxpbmdlciB3cm90ZToKPiA+
IE9uIDkvNC8wNywgTm9idXlvc2hpIE5ha2FkYSA8bm9idUBydWJ5LWxhbmcub3JnPiB3cm90ZToK
PiA+PiAidGFwIiBpbiAxLjkgaXMgc2FtZS4KPgo+IEdyZWF0IHRvIGhlYXIgdGhhdCB0aGlzIGlz
IHJlYWxseSBub3QgYXZhaWxhYmxlIHlldCA6KSBhbmQgSSdtIG5vdAo+IHRoZSBvbmx5IG9uZSB0
aGlua2luZyBpdCBzaG91bGQgYmUhCj4KPiA+IFRoZXkgaGF2ZSBkaWZmZXJlbnQgZWZmZWN0cywg
d2hpbGUgJ3RhcCcgd2lsbCByZXR1cm4gc2VsZiBhbmQgdGhyb3cKPiA+IGF3YXkgcmVzdWx0cyBv
ZiB0aGUgYmxvY2ssICd3aXRoJyByZXR1cm5zIHRoZSByZXN1bHQgb2YgdGhlIGJsb2NrLgo+ID4K
PiA+IFttYW52ZXJ1QHBpIH5dJCBSVUJZT1BUPSBpcmIxOQo+ID4gY2xhc3MgT2JqZWN0Cj4gPiAg
IGRlZiB3aXRoCj4gPiAgICAgeWllbGQoc2VsZikKPiA+ICAgZW5kCj4gPiBlbmQKPiA+ICMgbmls
Cj4gPiAxLnRhcHt8ZXwgZSArIDF9ICsgMgo+ID4gIyAzCj4gPiAxLndpdGh7fGV8IGUgKyAxfSAr
IDIKPiA+ICMgNAo+Cj4gU28gaXQncyBsaWtlIC53aXRoID09IC5zaW5nbGUtbWFwLCAgLnRhcCA9
PSAuc2luZ2xlLWVhY2gKPgo+IFJpZ2h0PwoKRXhhY3RseSA6KQoKXiBtYW52ZXJ1Cg==
 
N

Nobuyoshi Nakada

Hi,

At Tue, 4 Sep 2007 16:47:07 +0900,
Michael Fellinger wrote in [ruby-talk:267472]:
They have different effects, while 'tap' will return self and throw
away results of the block, 'with' returns the result of the block.
Sorry.

1.tap{|e| e + 1} + 2
# 3
1.with{|e| e + 1} + 2
# 4
1.tap{|e| break e + 1} + 2
# 4
 
P

Paul Brannan

Ruby tries to avoid local variables wherever it can by chaining
iterators together like in file-open-each-line-gsub. I tried to allow
a block to be used for non-enumerable types as well.

Ruby doesn't try to avoid local variables; this is something you have
done. IMO it's not a good idea. If there's a bug anywhere in your
chain and one of the methods you call returns the wrong type (nil, for
example), then debugging becomes more difficult.

Paul
 
M

Matthias Wächter

Ruby doesn't try to avoid local variables; this is something you have
done. IMO it's not a good idea. If there's a bug anywhere in your
chain and one of the methods you call returns the wrong type (nil, for
example), then debugging becomes more difficult.

Flow is generally from top to bottom (in a file) and from left to
right in a line. If there were not function calling and assignment
which work some kind of upstream, i.e. right to left. .with
introduces downstream function calling without the need for
rewriting code _around_ the whole statement and introducing more
variables.

It even helps debugging. If I am not sure about a value I can simply
add ".with {|x| puts "Debugging the expression: #{x}"}" within an
expression. Certainly, I might have to add a paren around the
expression for ordering stuff correctly, but I don't have to split
things apart.

FWIW, I have not touched the field of debugging with Ruby yet, so
allow me a second opinion on this later :)

- Matthias
 
P

Paul Brannan

It even helps debugging. If I am not sure about a value I can simply
add ".with {|x| puts "Debugging the expression: #{x}"}" within an
expression. Certainly, I might have to add a paren around the
expression for ordering stuff correctly, but I don't have to split
things apart.

I'm not arguing against the utility of "with", but against method
chaining, which in my experience causes more problems than it solves. I
tend to label code that uses method chaining more as "clever" than as
"beautiful".

My experience may or may not be indicative of the experience of the
average ruby programmer.

Paul
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
474,264
Messages
2,571,323
Members
48,005
Latest member
ChasityFan

Latest Threads

Top