Forum: Ruby limitations

Posted by Nokan Emiro (Guest)
on 2013-01-25 23:53
(Received via mailing list)
Hi,

Is there any way to somehow redefine operations like '&&' between my
objects?
'&&' is not defined as a method so I can't just overload it.  (Ruby does
not care
if an object responds to :&&, it is simply not used.)  Operators with
higher precedence
than '&&' usually work fine -- except '[]='.  Seems like above '&&' it 
is
the object's
responsibility to do operations and below that line it is the language
itself that
enforces the rules.  But is there a way to define custom code what 
should
de done by
an expression like 'x && y'?

The same limitation bothers me with '||', and with 'and', 'or' and 
'not'.
(Yes, I know that they are not operators but designed to be flow control
keywords,
however they are often used as operators...)

At the same time I'm struggling with the []=.  The strange thing with 
this
is
that the return value of the []= method is abandoned by Ruby but I 
expected
it to be the value of the expression that the []= method returns:

class X
  def []=(key, value)
    "I want to be the value!!"
  end
end
x = X.new
x[123] = 456   #=> 456

The value of the last expression is 456, and not the string.  I'd like 
to
get back the whole
x object instead of the right hand side value here.  Yes, I know that 
the
value of assignments
is the rhs value, but assignments in general are out of the scope of an
object, while []=
is an exceptional one.  It is done by the object itself, so it would be
logical to give the
power to the object to decide the value of the operation.  Is this a
deliberate behaviour or
just nobody cares the value of it?
Posted by Bartosz DziewoƄski (matmarex)
on 2013-01-26 00:01
(Received via mailing list)
On Fri, 25 Jan 2013 23:52:51 +0100, Nokan Emiro <uzleepito@gmail.com> 
wrote:

> responsibility to do operations and below that line it is the language
> itself that
> enforces the rules.  But is there a way to define custom code what should
> de done by
> an expression like 'x && y'?
>
> The same limitation bothers me with '||', and with 'and', 'or' and 'not'.
> (Yes, I know that they are not operators but designed to be flow control
> keywords,
> however they are often used as operators...)

Since I think Ruby 1.9 you can redefine the unary `!` operator (class A; 
def !; true; end end). This will also change the behavior of `not`.

`||` and `&&` are part of the language and you can't redefine them. 
(These short-circuit, which would be impossible to do if they were 
redefined.) They're handled separately throughout the parser.


> At the same time I'm struggling with the []=.  The strange thing with this
> is
> that the return value of the []= method is abandoned by Ruby but I expected
> it to be the value of the expression that the []= method returns:
> [...]
> Is this a
> deliberate behaviour or
> just nobody cares the value of it?

This is intended, to make assignments sane. The same applies to every 
method with name ending with '='. This is intended to make code like 
`a[0] = b[0] = c` or `a[0], b[0] = c, d` behave according to coders' 
expectations.
Posted by Robert Klemme (robert_k78)
on 2013-01-26 17:19
(Received via mailing list)
On Fri, Jan 25, 2013 at 11:52 PM, Nokan Emiro <uzleepito@gmail.com> 
wrote:
> Is there any way to somehow redefine operations like '&&' between my
> objects?

See Matma's reply.  Why do you think you need to redefine behavior of
these operators?

Kind regards

robert
Posted by Nokan Emiro (Guest)
on 2013-01-26 18:13
(Received via mailing list)
Hi Robert,

Why do you think you need to redefine behavior of these operators?
>

Because I'm working on a type that behaves like any other Ruby class (in
terms that it accepts all operations and messages that a Numeric, an 
Array,
a Hash or anything else), but it collects all it's history into a huge 
Ruby
expression for further evaluation.  It works charmingly with the normal
ops like :+, :-, :*, :/, :[], :<< etc., but, of course, it fails with 
those
outside
of an object's own world.

irb(main):001:0> x = Accumulator.new :x
=> <<<  x  >>>
irb(main):002:0> y = x + 123
=> <<<  x + 123  >>>
irb(main):003:0> z = 2 * y
=> <<<  2 * (x + 123)  >>>
irb(main):004:0> q = 1 - z ** 2
=> <<<  1 - (2 * (x + 123)) ** 2  >>>
irb(main):005:0> q+z+y
=> <<<  1 - (2 * (x + 123)) ** 2 + 2 * (x + 123) + x + 123  >>>
irb(main):006:0>

It may be useful to drop in "spy objects" (like the x above) into black 
box
functions,
like crypto functions, hash calculations, numerical approximations, or 
even
well-known
calculations, just to wrap them up in a huge expression instead of 
looking
at them as
the code of a long method, or just look for algebric simplifications on 
the
result.  It can
help improving the original calculation or finding out more about the
method's internals.

There are two huge limitations of this. One is that if the function 
makes
decision
based on the value of the input, this will fail.  For instance, the
expression x > 0
does not have a boolean value, but the expression <<<  x > 0  >>> 
itself,
which
is an Accumulator object, and has a logical value of true in conditional
expressions,
since it's not nil nor false.  The second limitation is that methods can
contain
lines like x = y && z, or with other words I have no control over them 
and
they
can use &&, ||, and, or, and not in calculations that I can incorporate
into the
object's internal expression describing it's calculation history.  The
solution for
the first one could be to break the calculation and ask the user what to
answer
for a relation like x > 0, or the Accumulator could do a parallel
calculation with
normal values (Numerics, Arrays, etc) just to return acceptable values 
for
the
tested function. That's the easier part.  But it seems that it's 
impossible
to grab
"operations" like &&... :(

irb(main):001:0> x = Accumulator.new :x
=> <<<  x  >>>
irb(main):002:0> y = x && 123
=> 123
irb(main):003:0> y
=> 123           <<<---------  this should be  <<<  x && 123  >>> 
somehow...
irb(main):004:0> x
=> <<<  x  >>>
irb(main):005:0> def fib(n)
irb(main):006:1>   n < 2 ? 1 : fib(n - 2) + fib(n - 1)
irb(main):007:1> end
=> nil
irb(main):008:0> (0..6).each { |i| p fib i }
1
1
2
3
5
8
13
=> 0..6
irb(main):009:0> fib x
=> 1    <<<<---------------  Well, this is because x < 2 is <<<  x < 2
 >>>, which is true (at least not false or nil), so the ?: in fib() 
returns
1  :(
irb(main):010:0> x < 2
=> <<<  x < 2  >>>
irb(main):011:0>
Posted by Robert Klemme (robert_k78)
on 2013-01-26 21:59
(Received via mailing list)
On Sat, Jan 26, 2013 at 6:12 PM, Nokan Emiro <uzleepito@gmail.com> 
wrote:
> of an object's own world.
> => <<<  1 - (2 * (x + 123)) ** 2 + 2 * (x + 123) + x + 123  >>>
> irb(main):006:0>

Ah, I see.

> It may be useful to drop in "spy objects" (like the x above) into black box
> functions,
> like crypto functions, hash calculations, numerical approximations, or even
> well-known
> calculations, just to wrap them up in a huge expression instead of looking
> at them as
> the code of a long method, or just look for algebric simplifications on the
> result.  It can
> help improving the original calculation or finding out more about the
> method's internals.

Are you aware of set_trace_func?  That can also be used for spying
although getting at return values is at least difficult.

I'm afraid, without hacking the interpreter you probably won't be able
to change behavior of && and ||. The only other alternative I can
think of right now is to parse the Ruby code yourself and execute the
AST "manually".

Kind regards

robert
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.