Extrage behaviour when concatenating a string pointed from a constant and a string (with no space after operator)

Large subject.

Could you tell me why the NoMethodError is happening? I'm using Ruby 2.1.2
in Ubuntu 14.04.

Thanks in advance!

2.1.2 :020 > A = 'A'
=> "A"
2.1.2 :021 > B = 'B'
=> "B"
2.1.2 :022 > 'A' + 'B'
=> "AB"
2.1.2 :023 > 'A' +'B'
=> "AB"
2.1.2 :024 > A + B
=> "AB"
2.1.2 :025 > A +B
NoMethodError: undefined method `+@' for "B":String
    from (irb):25
    from /home/juanjo/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :026 > A +'B'
NoMethodError: undefined method `+@' for "B":String
    from (irb):26
    from /home/juanjo/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :027 > 'A' +B
=> "AB"
2.1.2 :028 > a = 'a'
=> "a"
2.1.2 :029 > a +'B'
=> "aB"

Greets!

···

--
Juanjo Conti
blog: http://www.juanjoconti.com.ar

2.1.2 :025 > A +B
NoMethodError: undefined method `+@' for "B":String
    from (irb):25
    from /home/juanjo/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

RubyParser.new.parse "A + B"

=> s(:call, s(:const, :A), :+, s(:const, :B))

RubyParser.new.parse "A +B"

=> s(:call, nil, :A, s(:call, s(:const, :B), :+@))

So it is seeing "A(+B)" where "+" is a unary method, much like "-".

Same as if you typed `N +4`, it'd see `N(+4)`, call unary + on 4, then pass that as an arg to N.

···

On Jul 19, 2014, at 17:02, Juanjo Conti <jjconti@gmail.com> wrote:

Right, thanks. But why that is not happening with a +"B"?

···

2014-07-19 22:45 GMT-03:00 Ryan Davis <ryand-ruby@zenspider.com>:

On Jul 19, 2014, at 17:02, Juanjo Conti <jjconti@gmail.com> wrote:

> 2.1.2 :025 > A +B
> NoMethodError: undefined method `+@' for "B":String
> from (irb):25
> from /home/juanjo/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

>> RubyParser.new.parse "A + B"
=> s(:call, s(:const, :A), :+, s(:const, :B))

>> RubyParser.new.parse "A +B"
=> s(:call, nil, :A, s(:call, s(:const, :B), :+@))

So it is seeing "A(+B)" where "+" is a unary method, much like "-".

Same as if you typed `N +4`, it'd see `N(+4)`, call unary + on 4, then
pass that as an arg to N.

--
Juanjo Conti
blog: http://www.juanjoconti.com.ar

Right, thanks. But why that is not happening with a +"B"?

Because you've already taught the interpreter that 'a' is a local variable, so it knows it isn't `a(+"B")`, but `a + "B"`:

pp RubyParser.new.parse "a = 'a'; a +B"

s(:block,
s(:lasgn, :a, s(:str, "a")),
s(:call, s(:lvar, :a), :+, s(:const, :B)))

···

On Jul 19, 2014, at 18:50, Juanjo Conti <jjconti@gmail.com> wrote:

I got it!

A can't be a method, because it's upcase!

···

2014-07-19 23:10 GMT-03:00 Ryan Davis <ryand-ruby@zenspider.com>:

On Jul 19, 2014, at 18:50, Juanjo Conti <jjconti@gmail.com> wrote:

> Right, thanks. But why that is not happening with a +"B"?

Because you've already taught the interpreter that 'a' is a local
variable, so it knows it isn't `a(+"B")`, but `a + "B"`:

>> pp RubyParser.new.parse "a = 'a'; a +B"
s(:block,
s(:lasgn, :a, s(:str, "a")),
s(:call, s(:lvar, :a), :+, s(:const, :B)))

--
Juanjo Conti
blog: http://www.juanjoconti.com.ar

Dear Juanjo Conti,

def UPCASEMETHODISNOTAPROBLEM(arg)
  puts arg
end

UPCASEMETHODISNOTAPROBLEM "although not recommended"

Abinoam Jr.

···

On Sun, Jul 20, 2014 at 11:09 PM, Juanjo Conti <jjconti@gmail.com> wrote:

I got it!

A can't be a method, because it's upcase!

2014-07-19 23:10 GMT-03:00 Ryan Davis <ryand-ruby@zenspider.com>:

On Jul 19, 2014, at 18:50, Juanjo Conti <jjconti@gmail.com> wrote:

> Right, thanks. But why that is not happening with a +"B"?

Because you've already taught the interpreter that 'a' is a local
variable, so it knows it isn't `a(+"B")`, but `a + "B"`:

>> pp RubyParser.new.parse "a = 'a'; a +B"
s(:block,
s(:lasgn, :a, s(:str, "a")),
s(:call, s(:lvar, :a), :+, s(:const, :B)))

--
Juanjo Conti
blog: http://www.juanjoconti.com.ar

​Note quite​, upcase methods are allowed; see:
http://www.ruby-doc.org/core-2.1.2/Kernel.html#method-i-Integer Try
running: Integer -4.2

​Consider this code:

    a + 1
    # we've never seen 'a =' before, so we assume it's a function​
    # first call a(), then on the resulting object call .+(1)
    # same as: a().+(1)

    a +1
    # <word> <space> <plus> <any> = function call
    # same as: a(1.+@())

    C + 1
    # C starts with a capital, so we assume it's a const
    # same as: C.+(1)

    C +1
    # <word> <space> <plus> <any> = function call
    # same as: C(1.+@())

    b = 2
    b + 1
    # we *have* seen 'b =' before, so we know it's an lvar
    # same as: b.+(1)

    b +1
    # <lvar> <space> <plus> <any> = *not* function call
    # same as "b + 1": b.+(1)

​You could do a similar​ thing with @x, @@y, or $z. Those are unambiguously
variable names, so they'd never be interpreted as a function.

Have a think about what's going on here, if it helps:

irb(main):001:0> def a(*args) p [:a, *args]; end
irb(main):002:0> def b(*args) p [:b, *args]; end
irb(main):003:0> def C(*args) p [:C, *args]; end
irb(main):004:0> b = 1
irb(main):005:0> C = 99
irb(main):006:0> a +1
[:a, 1]
=> [:a, 1] # a(1.+@())
irb(main):007:0> b +1
=> 2 # b.+(1)
irb(main):008:0> C +1
[:C, 1]
=> [:C, 1] # C(1.+@)
irb(main):009:0> a + 1
[:a]
TypeError: no implicit conversion of Fixnum into Array
from (irb):9:in `+'
from (irb):9
from /usr/local/bin/irb-trunk:11:in `<main>'
# a().+(1)
irb(main):010:0> b + 1
=> 2 # b.+(1)
irb(main):011:0> C + 1
=> 100 # C.+(1)

···

On 21 July 2014 12:09, Juanjo Conti <jjconti@gmail.com> wrote:

​​
I got it!

A can't be a method, because it's upcase!

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

You're right.

···

2014-07-20 23:46 GMT-03:00 Abinoam Jr. <abinoam@gmail.com>:

Dear Juanjo Conti,

def UPCASEMETHODISNOTAPROBLEM(arg)
  puts arg
end

UPCASEMETHODISNOTAPROBLEM "although not recommended"

Abinoam Jr.

On Sun, Jul 20, 2014 at 11:09 PM, Juanjo Conti <jjconti@gmail.com> wrote:
> I got it!
>
> A can't be a method, because it's upcase!
>
>
> 2014-07-19 23:10 GMT-03:00 Ryan Davis <ryand-ruby@zenspider.com>:
>
>>
>> On Jul 19, 2014, at 18:50, Juanjo Conti <jjconti@gmail.com> wrote:
>>
>> > Right, thanks. But why that is not happening with a +"B"?
>>
>> Because you've already taught the interpreter that 'a' is a local
>> variable, so it knows it isn't `a(+"B")`, but `a + "B"`:
>>
>> >> pp RubyParser.new.parse "a = 'a'; a +B"
>> s(:block,
>> s(:lasgn, :a, s(:str, "a")),
>> s(:call, s(:lvar, :a), :+, s(:const, :B)))
>>
>
>
>
> --
> Juanjo Conti
> blog: http://www.juanjoconti.com.ar

--
Juanjo Conti
blog: http://www.juanjoconti.com.ar

This email is really clear. Thanks!

···

2014-07-20 23:56 GMT-03:00 Matthew Kerwin <matthew@kerwin.net.au>:

On 21 July 2014 12:09, Juanjo Conti <jjconti@gmail.com> wrote:

​​
I got it!

A can't be a method, because it's upcase!

​Note quite​, upcase methods are allowed; see:
Module: Kernel (Ruby 2.1.2) Try
running: Integer -4.2

​Consider this code:

    a + 1
    # we've never seen 'a =' before, so we assume it's a function​
    # first call a(), then on the resulting object call .+(1)
    # same as: a().+(1)

    a +1
    # <word> <space> <plus> <any> = function call
    # same as: a(1.+@())

    C + 1
    # C starts with a capital, so we assume it's a const
    # same as: C.+(1)

     C +1
    # <word> <space> <plus> <any> = function call
    # same as: C(1.+@())

    b = 2
    b + 1
    # we *have* seen 'b =' before, so we know it's an lvar
    # same as: b.+(1)

    b +1
    # <lvar> <space> <plus> <any> = *not* function call
    # same as "b + 1": b.+(1)

​You could do a similar​ thing with @x, @@y, or $z. Those are
unambiguously variable names, so they'd never be interpreted as a function.

Have a think about what's going on here, if it helps:

irb(main):001:0> def a(*args) p [:a, *args]; end
irb(main):002:0> def b(*args) p [:b, *args]; end
irb(main):003:0> def C(*args) p [:C, *args]; end
irb(main):004:0> b = 1
irb(main):005:0> C = 99
irb(main):006:0> a +1
[:a, 1]
=> [:a, 1] # a(1.+@())
irb(main):007:0> b +1
=> 2 # b.+(1)
irb(main):008:0> C +1
[:C, 1]
=> [:C, 1] # C(1.+@)
irb(main):009:0> a + 1
[:a]
TypeError: no implicit conversion of Fixnum into Array
from (irb):9:in `+'
from (irb):9
from /usr/local/bin/irb-trunk:11:in `<main>'
# a().+(1)
irb(main):010:0> b + 1
=> 2 # b.+(1)
irb(main):011:0> C + 1
=> 100 # C.+(1)

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

--
Juanjo Conti
blog: http://www.juanjoconti.com.ar

Ok... can I say that a method in upercase with no arguements can't be
called?

2.1.2 :010 > def Aja(*args)
2.1.2 :011?> puts 'aja'
2.1.2 :012?> end
=> :Aja
2.1.2 :013 > Aja
NameError: uninitialized constant Aja
    from (irb):13
    from /home/juanjo/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :014 > Aja 1
aja
=> nil

···

2014-07-20 23:46 GMT-03:00 Abinoam Jr. <abinoam@gmail.com>:

Dear Juanjo Conti,

def UPCASEMETHODISNOTAPROBLEM(arg)
  puts arg
end

UPCASEMETHODISNOTAPROBLEM "although not recommended"

Abinoam Jr.

On Sun, Jul 20, 2014 at 11:09 PM, Juanjo Conti <jjconti@gmail.com> wrote:
> I got it!
>
> A can't be a method, because it's upcase!
>
>
> 2014-07-19 23:10 GMT-03:00 Ryan Davis <ryand-ruby@zenspider.com>:
>
>>
>> On Jul 19, 2014, at 18:50, Juanjo Conti <jjconti@gmail.com> wrote:
>>
>> > Right, thanks. But why that is not happening with a +"B"?
>>
>> Because you've already taught the interpreter that 'a' is a local
>> variable, so it knows it isn't `a(+"B")`, but `a + "B"`:
>>
>> >> pp RubyParser.new.parse "a = 'a'; a +B"
>> s(:block,
>> s(:lasgn, :a, s(:str, "a")),
>> s(:call, s(:lvar, :a), :+, s(:const, :B)))
>>
>
>
>
> --
> Juanjo Conti
> blog: http://www.juanjoconti.com.ar

--
Juanjo Conti
blog: http://www.juanjoconti.com.ar

​You can add parentheses:

irb(main):001:0> def Aja *args
irb(main):002:1> puts 'aja'
irb(main):003:1> end
=> :Aja
irb(main):004:0> Aja()
aja
=> nil

But generally speaking, yes, you need the arguments list to tell the parser
that it's a function call.

···

On 21 July 2014 13:20, Juanjo Conti <jjconti@gmail.com> wrote:

Ok... can I say that a method in upercase with no arguements can't be
called?

2.1.2 :010 > def Aja(*args)
2.1.2 :011?> puts 'aja'
2.1.2 :012?> end
=> :Aja
2.1.2 :013 > Aja
NameError: uninitialized constant Aja
    from (irb):13

    from /home/juanjo/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :014 > Aja 1
aja
=> nil

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/