Are There "Rules" for Ruby Syntactic Sugar

Are there rules for Ruby syntactic sugar?

There are a couple methods that ruby lets you call in a special way. These are the [] as you mentioned, the +, -, == and the like as someone else mentioned. Another important example are methods of the form something=(value) which can be called with object.something = value and allow you to create accessors.

Edit:

Fun fact 1: if you define a + method you get += for free.

Fun fact 2: if you define a <=> you get all comparison methods, courtesy of Comparable

what is [] operator in Ruby

It depends if you're talking about array literals or those brackets used for getting or assigning values from/to arrays or hashes.

As a literal, [] is just an array literal, shorthand for writing Array.new. You also asked about {}, which is a hash literal, i.e. shorthand for writing Hash.new. Parentheses are not literals (or 'operators' for that matter) – they're used to group things.

As an 'operator' for getting or assigning values, as others have pointed out in the comments, [] isn't special or conceptually different from other 'operators'.

In the same way 2 + 3 is sugar for writing 2.+(3), writing array[0] is sugar for writing array.[](0).

The reason this works is that arrays have a method that's literally called []. You can find it by getting all the methods on an array:

[].methods
# => long array including :[] (and also :[]=, by the way, for assignments)

Note that the brackets in [].methods are an array literal, not an 'operator'.

I haven't looked at the implementation of any Ruby interpreters, but my guess is they see something like array[0] and convert it to array.[](0) (or at least they treat it as such). That's why this kind of sugar syntax works and looks like 'operators' even though it's all methods under the hood.

What are the restrictions for method names in Ruby?

Method names in Ruby may contain upper-case and lower-case letters, numbers, underscores _ and the punctation signs !, ?, =.

A method name can't begin with a number, and the characters !, ? and = can only appear at the end.

Non-ASCII characters can be used in a method name, but this can lead to very confusing situations and should not be common practice.

It's good practice, while not mandatory, to start the method name with a lower-case character, because names that start with capital letters are constants in Ruby. It's still possible to use a constant name for a method, but you won't be able to invoke it without parentheses, because the interpeter will look-up for the name as a constant:

def Capital
nil
end

Capital # NameError: uninitialized constant Capital
Capital() # => nil

Some very widely and consistently used conventions when defining method names are:

  1. Method names are full down-case, with underscores _ as separators for words into the name (e.g. Math::sqrt, Array#each_index, ...).

  2. Predicates have a question mark ? as last character (e.g. Array#empty?, Hash#has_key?, ...). While predicates usually return boolean values, this is not always the case: these methods just need to return nil or false if the predicate evaluates to false, any other value otherwise (e.g. File::size? returns nil if the file does not exist, the size of the file as an Integer otherwise).

  3. Methods that modify the state of the object on which they are invoked, or that have an unusual behavior have an exclamation mark ! as last character; this methods are sometimes called mutators because they usually are destructive or in-place versions of other methods (e.g. Array#sort!, Array#slice!, ...).

  4. Setters have an equal sign = as last character (e.g. Array#[]=, ...); the Ruby interpeter offers syntactic sugar for invokation of setter methods:

    a = [4, 5, 6]
    a[0] = 3 # Shorthand for a.[]=(0, 3)

Ruby also allows to define operators using the operator symbol as the method name:

╔═══════════════════════════╦═════════════════════════════════════════════╦═══════╗
║ Operators (by precedence) ║ Operations ║ Arity ║
╠═══════════════════════════╬═════════════════════════════════════════════╬═══════╣
║ ! ~ + ║ Boolean NOT, bitwise complement, unary plus ║ 1 ║
║ ║ (define with method name +@, Ruby 1.9+) ║ ║
║ ║ ║ ║
║ ** ║ Exponentiation ║ 2 ║
║ ║ ║ ║
║ - ║ Unary minus (define with method name -@) ║ 1 ║
║ ║ ║ ║
║ * / % ║ Multiplication, division, modulo ║ 2 ║
║ ║ ║ ║
║ + - ║ Addition, subtraction ║ 2 ║
║ ║ ║ ║
║ << >> ║ Bitwise shift ║ 2 ║
║ ║ ║ ║
║ & ║ Bitwise AND ║ 2 ║
║ ║ ║ ║
║ | ^ ║ Bitwise OR, Bitwise XOR ║ 2 ║
║ ║ ║ ║
║ < <= => > ║ Ordering ║ 2 ║
║ ║ ║ ║
║ == === != =~ !~ <=> ║ Equality, pattern matching, comparison ║ 2 ║
╚═══════════════════════════╩═════════════════════════════════════════════╩═══════╝

Unary operator methods are passed no arguments; binary operator methods are passed an argument, and operate on it and on self.

It's important to adhere strictly to the arity of the operators; while it is possible to define operator methods with a different arity (e.g. a + method that takes two arguments), Ruby would not allow you to call the method with operator syntax (it would however work with dot syntax).

It's good practice to adhere to the original semantics of the operators as much as possible: it should be intuitive to someone who knows the original meaning of the operator how it works with user defined classes.

The language also offers syntactic sugar for the special, non-operator ,[] method that is normally used for accessing array and hash values. The [] method can be defined with arbitrary arity.

For every binary operator in the table, except ordering, equality, comparison and pattern matching, Ruby also offers shorthand for abbreviated assignment (e.g. x += y expands to x = x + y); you can't define them as methods, but you can alter their behavior defining the operators on which they're based.

None of these characters can be used inside normal method names (e.g. do&print or start-up are not valid method names).

Understanding Implicit hash within a ruby method definition

Haven't watched that video, but "implicit hash" is a poor choice of words. This is a feature called "keyword arguments" and it has a pretty specific syntax which, yes, resembles a hash, but isn't one.

For example, it allows required arguments, which is impossible in a hash.

def foo(required:, optional: 1)
# some code
end

Overriding a hash and making [] operators private - cant use ||= anymore

Handling of private methods is a bit of a mess, currently.

The original rule was:

private methods can only be called without an explicit receiver.

This is a nice, simple, easily understandable rule. It is also a static rule, i.e. it can be checked without running the code, in fact, it is even a syntactic rule, it doesn't even need sophisticated static analysis, it can be checked in the parser.

However, it was soon noticed that this rule makes it impossible to call private setters, since setters can't be called without an explicit receiver (foo = bar is a setting a local variable, not calling a setter). Ergo, the rule was extended:

private methods can only be called without an explicit receiver, unless the method call is an assignment method call, in which case the method can also be called with an explicit receiver as long as that explicit receiver is the literal pseudo-variable self.

This allows you to call private setters with an explicit receiver of the literal value self:

self.foo = bar

but not a dynamic value of self

baz = self
baz.foo = bar # NoMethodError: private method `foo=' called

This still preserves the property that private method calls can be detected at parse time.

Two years ago, I filed a bug about abbreviated method assignments not working, i.e.:

self.foo += bar # NoMethodError

That bug was fixed by again extending the rule for private method calls (and now the rule is already getting so complex that I'm not going to spell it out).

However, there are still a lot of cases left that are not covered by the existing rules, where methods simply cannot syntactically be called without an explicit receiver and thus cannot be private:

self[foo]
!self
self + foo

etc.

Some of those have been fixed, some haven't. The problem is that the rule has now gotten so complex that it is hard to implement correctly. There have been proposals to change the rule to something like this:

private methods can only be called without an explicit receiver or an explicit receiver which is the literal pseudo-variable self.

That is a nice, simple, easily understandable rule, can be statically checked at parse time, and has none of the complex exceptions and corner cases we have currently. It is, however, not yet implemented AFAIK.

block as argument to method in ruby is a mystery?

As Sawa says, blocks aren't objects, and thus aren't passed as arguments. Blocks are a special thing in Ruby. They have special syntax (which only allows us to pass one block to a method) and a special keyword dedicated to calling them. Here's an example implementation of times in Ruby:

def times
if block_given?
i = 0
while i < self
yield i
i += 1
end
# times returns the number that was executing times,
# so we need to return self here
self
else
enum_for :times
# ^^ This is where the Enumerator comes from if
# you don't pass a block.
end
end

The block_given? method tests if there is a block associated with the current method, and the yield keyword calls the block.

Do the 'Array#dig' or 'Hash#dig' methods violate the Law of Demeter?

dig is pretty much just syntactic sugar on the array and hash accesses you performed before the method was introduced, so it doesn't violate the Law of Demeter any more or less than you already were. Consider your example:

[1, {foo: :bar}].dig(1, :foo)
[1, {foo: :bar}][1][:foo]

You have the same data structure, you're getting the same value, but pleasantly, now you don't have to check for nil every step of the way too.

The Law of Demeter is a design heuristic. If you find yourself reaching through three objects to accomplish something, the code doing the reaching has to know things about all three of those objects. It's a sign that you might want to refactor to reduce the number of dependencies your modules have, but the method you use to do the reaching, whether [] or dig, isn't entirely relevant.

Do all Ruby interpreters follow the same Ruby syntax?

Do all Ruby interpreters follow the same Ruby syntax defined in www.ruby-lang.org?

Yes, they all use the same syntax. In fact, they actually all use the same parser, or at least a parser that was automatically generated from the same source file.

Cause I'm using: http://ruby-doc.org/ruby-1.9/index.html.

Which interpreters are implementing that one?

At the moment, the only production-ready Ruby execution engine that implements Ruby 1.9 fully is YARV.

JRuby itself is production-ready, and it implements both Ruby 1.8.7 and Ruby 1.9.2, but the Ruby 1.9.2 implementation is not yet complete. IronRuby and Rubinius are also working on implementations of Ruby 1.9.2. MacRuby has a fairly complete Ruby 1.9 implementation, but it is still far from a 1.0 release. MRI doesn't implement Ruby 1.9 and probably never will.

But I don't understand why you are so concerned about the syntax. Syntax differences are trivial to spot: if there were a difference in the syntax, the engine would simply refuse to parse your file and you would know immediately that there is something wrong. Differences in semantics on the other hand are much more dangerous.

Are there any Ruby language features you avoid?

The whole range of "$" globals (see Pickaxe2 pp333-336) mostly inherited from Perl, are pretty ghastly, although I have on occasion found myself using $: instead of $LOAD_PATH.



Related Topics



Leave a reply



Submit