Search code examples
rubygeneric-programming

Can Ruby define some generic operator?


I just learned about Ruby's "spaceship operator", <=>.

I find it interesting. Can Ruby define our own special operators like >=< for opposite of <=>?

Could it be applied to generic types like Java templates?

How do I do it?


Solution

  • There is a fixed set of operators in Ruby, some of which are syntactic sugar for message sends and can thus be overridden.

    You can't add new operators. In Ruby, the fixed set of operators is part of the language syntax, and Ruby doesn't allow Ruby code to change the syntax of the language. If you want to add a new operator, you will have to convince matz to change the language specification, and you will have to convince the developers of Rubinius, JRuby, YARV, MagLev, and MRuby to implement this change.

    Overridable

    These desugar into message sends and thus can be overridden by implementing the corresponding methods.

    • unary prefix
      • +foofoo.+@(), ergo: def +@; end
      • -foofoo.-@(), ergo: def -@; end
      • !foofoo.!(), ergo: def !; end
      • ~foofoo.~(), ergo: def ~; end
    • binary infix
      • foo + barfoo.+(bar), ergo: def +(other) end
      • foo - barfoo.-(bar), ergo: def -(other) end
      • foo * barfoo.*(bar), ergo: def *(other) end
      • foo / barfoo./(bar), ergo: def /(other) end
      • foo % barfoo.%(bar), ergo: def %(other) end
      • foo ** barfoo.**(bar), ergo: def **(other) end
      • foo >> barfoo.>>(bar), ergo: def >>(other) end
      • foo << barfoo.<<(bar), ergo: def <<(other) end
      • foo & barfoo.&(bar), ergo: def &(other) end
      • foo ^ barfoo.^(bar), ergo: def ^(other) end
      • foo | barfoo.|(bar), ergo: def |(other) end
      • foo < barfoo.<(bar), ergo: def <(other) end
      • foo > barfoo.>(bar), ergo: def >(other) end
      • foo <= barfoo.<=(bar), ergo: def <=(other) end
      • foo >= barfoo.>=(bar), ergo: def >=(other) end
      • foo == barfoo.==(bar), ergo: def ==(other) end
      • foo === barfoo.===(bar), ergo: def ===(other) end
      • foo != barfoo.!=(bar), ergo: def !=(other) end
      • foo =~ barfoo.=~(bar), ergo: def =~(other) end
      • foo !~ barfoo.!~(bar), ergo: def !~(other) end
      • foo <=> barfoo.<=>(bar), ergo: def <=>(other) end
    • n-ary "around-fix"
      • foo[bar, baz]foo.[](bar, baz), ergo: def [](a, b) end
      • foo[bar, baz] = quuxfoo.[]=(bar, baz, quux), ergo: def []=(a, b, c) end
      • foo.(bar, baz)foo.call(bar, baz), ergo: def call(a, b) end

    Non-overridable

    These don't desugar into message sends.

    • unary prefix
      • defined?
    • binary infix
      • foo && bar
      • foo and bar
      • foo || bar
      • foo or bar
      • foo = bar
    • compound assignment
      • foo ||= bar
      • foo &&= bar

    Sort-of overridable

    You can't override these independently, but they (at least partially) translate into other operators that can be overridden.

    • unary prefix
      • not foofoo.!(), ergo: def !; end
    • compound assignment
      • foo ω= barfoo = foo ω bar for any ω ∉ { ||, && }