Forum: Ruby readable and provocative & daring

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Richard D. (Guest)
on 2006-01-24 18:03
(Received via mailing list)
A funny thing happened to me in irb this week. Well, to be honest it's
a reconstruction. And it was little more than one thing. But hopefully
you'll get the gist.

irb(main):001:0> readable = false
=> false
irb(main):002:0> readable and provocative & daring
=> false
irb(main):003:0> readable = nil
=> nil
irb(main):004:0> readable and provocative & daring
=> nil
irb(main):005:0> readable = "yes"
=> "yes"
irb(main):006:0> readable and provocative & daring
NameError: undefined local variable or method `provocative' for
main:Object
        from (irb):6
irb(main):007:0> provocative = false
=> false
irb(main):008:0> readable and provocative & daring
NameError: undefined local variable or method `daring' for main:Object
        from (irb):8

Fine so far. & is eager, it always seeks to evaluate its right hand
side. Fair cop. Let's move on.

irb(main):009:0> daring = nil
=> nil
irb(main):010:0> readable and provocative & daring
=> false
irb(main):011:0> provocative = nil
=> nil
irb(main):012:0> readable and provocative & daring
=> false

Hmm, a slightly surprising difference with and there. But does it
matter? It didn't to me at the time. I was Ruby newbie with an
important business problem to solve ...

irb(main):013:0> provocative = "yes "
=> "yes "
irb(main):014:0> readable and provocative & daring
NoMethodError: undefined method `&' for "yes ":String
        from (irb):14

Aha. So I defined a little method in String and then ...

irb(main):020:0> readable and provocative & daring
=> nil
irb(main):021:0> daring = "please"
=> "please"
irb(main):022:0> readable and provocative & daring
=> "yes please"

Yep, that's it. Now let's really take this thing out for a spin:

irb(main):023:0> daring = [1000, " times ", "yes"]
=> [1000, " times ", "yes"]
irb(main):024:0> readable and provocative & daring
=> "yes 1000 times yes"
irb(main):025:0> provocative = ["please", "say", "yes"]
=> ["please", "say", "yes"]
irb(main):026:0> readable and provocative & daring
=> ["yes"]

Hmm.

The elided code was of course

class String
  def & a
    self + a.to_s if a
  end
end

This was I stress again to solve a real world problem. Or, to be more
precise, to make an existing solution to a real world problem more
readable.

But it did feel a bit provocative and daring.

Any comments?

Richard

----

Ruby for laser purity
Justin C. (Guest)
on 2006-01-24 22:53
(Received via mailing list)
Some interesting stuff there...but I think the main thing is that 'and'
is a Ruby keyword, whereas '&' is an instance method. '&' can be defined
as makes sense for whatever object it is being called on. Therefore,
something like

 >readable and provocative & daring

is equivalent to

 >readable and provocative.&(daring)

which means if readable is false, then provocative.& doesn't need to be
called at all. However, if readable is true, then it does something like

 >provocative.send(:&, daring)

If provocative doesn't exist, it can't call any methods on it. If
provocative does exist, but daring doesn't, then daring cannot be
evaluated and can't be passed in as a parameter to the function call.

However, once provocative and daring are defined, it can try to call the
provocative.& method, if it exists. And since you define it, all is
good!

My apologies if you already realized all that! The way you have the
syntax and the fact that "and" and "&" are equivalent in English could
be confusing...

-Justin
Richard D. (Guest)
on 2006-01-25 08:55
(Received via mailing list)
> My apologies if you already realized all that!

I did kinda realise all that, except I wasn't focusing on the fact that
'and' is a keyword. Is it only with keywords that the rhs of the
operator may not be evaluated? I don't think so - && and || have this
property. But I guess they cannot ever be redefined, right?

I had small questions and BIG questions here.

My BIG questions have to do with what seemed at the time a daring
extension of the meaning of '&'. Is such an extension sensible: -

1. just for my project, with four other Rubyists of differing vintages?
(I won't tell you what they've said so far, that could spoil the fun!)

2. if we were to release some of our work as open source to the whole
wide wonderful world of Ruby programmers?

I was starting with a reasonably complex conditional method that used
'+' on strings that may be nil. I soon came to realise that

a + b

works fine when they're both bona fide strings but gives an error when
either is nil. On the other hand

nil & b

is already defined as false, which seems operationally equivalent to
nil in every way so is exactly what I want. (Another of my small
questions, in passing. Are nil and false ever different in their
behavior?)

Extending '&' as I have - and the opening up of a core class and method
definition is so short and simple, I do love Ruby for that - we now
also have

"hello " & "cheeky" -> "hello cheeky"
"hello " & nil -> nil

What's great about this is the amount of brackets and branching it
removes from the original method.

What's weird about it is this.

12 & 7 -> 4

[3, 6, 9] & [2, 4, 6] -> [6]

In other words, '&' always reduces, the result is always <= in some
reasonable sense both arguments.

Until now.

But I still think that, in the highly pragmatic spirit of Ruby,
everyone should have such a useful meaning for '&' on Strings. I surely
does not harm and fits in brilliantly with the much used

blob || "not known"

and

blob ||= "starter for ten"

idioms.

But I'm a newb!

Thanks very much for the feedback.

Richard
Matthew M. (Guest)
on 2006-01-25 17:38
(Received via mailing list)
&& and 'and' both exhibit the same behaviour, in that they
short-circuit.

& is a combiner-and, not logical (rather, typically bitwise), which
requires it evaluate both sides of the expression.

Maybe you really wanted:  readable and provocative && daring

Yes, of course, you can make your own versions, but you need to keep
this behaviour in mind.


def foo
  p "foo"
  false
end

foo && foo
> "foo"

foo || foo
> "foo"
> "foo"

foo & foo
> "foo"
> "foo"

foo and foo
Matthew M. (Guest)
on 2006-01-25 17:59
(Received via mailing list)
> Yes, of course, you can make your own versions, but you need to keep
> this behaviour in mind.

Sorry, should have realized that you can make your own version of &
but not of &&.
Richard D. (Guest)
on 2006-01-25 23:08
(Received via mailing list)
> Sorry, should have realized that you can make your own version
> of & but not of &&.

Yes, and it was the ability to extend & to String that started as a
'what if?' this time last week and ended with some serious questions
about how wide the utility of this version may be. Or perhaps Ruby
veterans would vote it down for reasons I might not currently grasp?

Richard

----

Ruby for laser purity
Matthew M. (Guest)
on 2006-01-25 23:26
(Received via mailing list)
On 1/25/06, Richard D. <removed_email_address@domain.invalid> wrote:
> Yes, and it was the ability to extend & to String that started as a
> 'what if?' this time last week and ended with some serious questions
> about how wide the utility of this version may be. Or perhaps Ruby
> veterans would vote it down for reasons I might not currently grasp?

Well, if you simply wanted to create a & method on String, that's easy:

class String
  def &(rhs)
    self + rhs
  end
end

If what you actually wanted is to make '&' short-circuit, that's a
whole 'nother problem that, IMO, is best left alone.  First, it flies
in the face of the traditional meaning from other long-lived
programming languages. (I'm very willing to discard or change
tradition if it makes gains on efficiency, readability, etc...  but I
highly disagree that making '&' short-circuit is worth it, especially
when Ruby has both '&&' and 'and'.)

Second, it could not be (simply) implemented with a standard method
call. The nature of calling a function is that its arguments are
evaluated.  Which means that 'rhs' in String.& above must be evaluated
before the function can be called.  So String.& cannot possibly
short-circuit.  (Related... If you read any good-programming-practices
book for C++, they warn against overloading operator&& and operator||
for specifically this reason: they cannot short-circuit.)

You could, possibly, redefine String.& like this:

class String
  def &(rhs)
    some_cond ? self + rhs : self
  end
end

And then, if rhs is an object that lazily evaluates, you might be able
to fake short-circuitness.  But I think it's not worth it that much...
 smells bad, probably better ways to do things.
This topic is locked and can not be replied to.