It’s just syntactic sugar. There are certain syntax patterns that get translated into message sends. In particular
a + b
is the same as
a.+(b)
and the same applies to ==, !=, <, >, <=, >=, <=>, ===, &, |, *, /, -, %, **, >>, <<, !==, =~ and !~ as well.
Also,
!a
is the same as
a.!
and the same applies to ~.
Then,
+a
is the same as
a.+@
and the same applies to -.
Plus,
a.(b)
is the same as
a.call(b)
There is also special syntax for setters:
a.foo = b
is the same as
a.foo=(b)
And last but not least, there is special syntax for indexing:
a[b]
is the same as
a.[](b)
and
a[b] = c
is the same as
a.[]=(b, c)