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
on 2006-01-24 18:03
on 2006-01-24 22:53
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
on 2006-01-25 08:55
> 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] ->  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
on 2006-01-25 17:38
&& 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
on 2006-01-25 17:59
> 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 &&.
on 2006-01-25 23:08
> 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
on 2006-01-25 23:26
On 1/25/06, Richard D. <firstname.lastname@example.org> 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.