Ruby's not keyword is not not but ! (not)

The title is confusing enough, so I don't want to bore you you with words today, let's better listen to some deep-sounding Rspec code... 🎵🎶

# ~/code/ruby/rubys_not_is_not_not_but_!_(not)_spec.rb

require './rubys_not_is_not_not_but_!_(not)'

RSpec.describe 'Ruby\'s not keyword' do
  let(:banger) { Banger.new }
  let(:naysayer) { Naysayer.new }

  context 'when ! is overriden' do
    it 'returns whatever value is returned by !' do
      expect(!banger).to eq :bang
      expect((not banger)).to eq :bang
    end

    it 'also returns the overriding\'s return value when called on the instance' do
      expect(banger.!).to eq :bang
    end
  end

  context 'when not is overriden' do
    it 'returns the overriding\'s return value when called on the instance' do
      expect(naysayer.not).to eq :nay
    end

    context 'but when used in the classical way before the message' do
      it 'evaluates a truthy value to false (as it always does)' do
        expect((not naysayer)).to eq false
      end
    end
  end
end
Live test-driven, read tests first

Which gives us this result:

And of course you are curious how it's implemented:

# ~/code/ruby/rubys_not_is_not_not_but_!_(not).rb

class Banger
  def !
    :bang
  end
end

class Naysayer
  def not
    :nay
  end
end
Check this masterful code on GitHub if you don't believe

The Moral

As you can read from the docs, Ruby's ! can be overriden so that it will even return the overriding's value when used in front of a message (like ! message(), but the not keyword overridings only have effect on the instance's return value (e.g. instance.not()).

So the moral of the story is that not uses ! somehow under the hood because when ! is overriden it has also an effect on not.

This being said, they still are not aliases since they differ a tad in functionality (namely, in precedence).