Is it okay to define a [] method in ruby’s NilClass?

Ruby by default does not include the method [] for NilClass

For example, to check if foo["bar"] exists when foo may be nil, I have to do:

foo = something_that_may_or_may_not_return_nil
if foo && foo["bar"]
  # do something with foo["bar"] here
end

If I define this method:

class NilClass
  def [](arg)
    nil
  end
end

Something like that would make this possible, even if foo is nil:

if foo["bar"]
  # do something with foo["bar"]
end

Or even:

if foo["bar"]["baz"]
  # do something with foo["bar"]["baz"] here
end

Question:
Is this a good idea or is there some reason ruby doesn’t include this functionality by default?

EDIT July 2017: My core complaint was the unwieldy foo && foo["bar"] && foo ["bar"]["baz"] idiom required to access nested values when at any point the value may be nil. As of Ruby 2.3, Array#dig and Hash#dig exist, which addresses my concern. Now I can use the following idiom!

if foo && foo.dig("bar", "baz")
  # do something
end

# Note that errors will still occur if a value isn't what you expect. e.g.
foo = { "a" => { "b" => "c" } }
foo && foo.dig("a", "b", "c") # raises TypeError: String does not have #dig method

9

Nullable values must be addressed carefully, it’s not the same a variable that always has a certain type than other which sometimes has this type, sometimes is null. Functional languages are usually the most careful on this regard and they have custom types to manage it (Maybe in Haskell, Option in Ocaml/F#/Scala, …).

In Ruby I wouldn’t extend the NilClass as you propose, it may lead to buggy code (note that you’re modifying a basic object used by every library, it’s a disaster waiting to happen). However, it’s understandable that you want some pattern to succinctly interact with values that may be nil to avoid littering the code with conditionals.

One option is the maybe proxy pattern proposed in Ick:

hash.maybe[key1].maybe[key2].maybe[key3]

Or the block syntax:

hash.maybe { |h| h[key1][key2][key3] }

3

In some ruby code that I’ve had to tweak, I have added isFalse or something of that nature to the nil object to make some parts be not unhappy with each other.

Open classes are not my favorite thing and making such a change to the nil class is not a small change. It is a significant change because it can change the entire functioning of the entire system.

You can do it. Make sure you regression test everything and document up front all changes to core objects so that other people don’t become surprised when suddenly nil behaves in an unexpected way.

As to why that isn’t default functionality – that is the realm of speculation into the mind of Matz.

1

If this is for a specific condition, and I suspect it is, then delegate the behavior. Check that the object is defined, and then check if it is nil, and work accordingly.

It will preserve the expected behavior in core, and give you the specific changed behavior when you want it.

If later you need to modify the behavior to include False, you have one place to go to account for the needed change.

Changing core behavior for a specific need should always be considered carefully.

Why does something_that_may_or_may_not_return_nil have to return nil? And if you don’t want to change that method, just wrap it in another method that returns an empty hash if the wrapped method returns nil.

As for nesting, you can use Hash‘s default_proc to return empty hashes when a key isn’t found:

# self-referential, to provide arbitrary depth
default_proc = ->(h,k){ Hash.new(&default_proc) }
some_hash.default_proc = default_proc

The above lets you access deeply undefined entries without making any changes. But you also need to use empty? instead of treating the result as a simple boolean.

some_hash[:a][:b][:c].empty?

If you want a solution that will also store those new empty hashes into the parent, use this default_proc instead:

default_proc = ->(h,k){ h[k] = {}.tap{|newh| newh.default_proc = default_proc } }
# This will auto-vivify:
some_hash = {}
some_hash.default_proc = default_proc
some_hash[:a][:b][:c]
some_hash  # {:a=>{:b=>{:c=>{}}}}

Also, Rails provides the try method, that only calls the named method if the receiver isn’t nil. Yeah, it’s a lot uglier.

foo.try(:[], 'bar').try(:[], 'baz')

It might be better to wrap the arbitrarily-deep hash into a class that hides away the ugliness. For example, does the ['bar']['baz'] entry have some real-world meaning? If so, you can put a method on that class and have that method reach into that depth without the caller needing to worry about it.

I’d recommend you use Hash#fetch to access hash entries when you’re unsure they’ll be present. This way if the requested key is not found you’ll get a KeyError which you can handle appropriately. When you blindly access nonexistent keys you get subtly confusing errors that wind up with you trying to act on unexpected nils in odd places elsewhere in your code.

> { a: { b: 1 } }[:a][:z].to_s
=> ""
> { a: { b: 1 } }.fetch(:z).fetch(:b).to_s
KeyError: key not found: :z

Aside: I developed my taste for Hash#fetch after watching Avdi Grimm’s RubyTapas Episode #8. I pay money for that and so far it’s worth it.

I wrote a gem designed for just this case. I came across this question when looking for a solution, so I thought I’d post here on what I ended up with.

Them gem is called Dottie and is available at https://github.com/nickpearson/dottie.

Here’s a quick sample of how it’s designed to be used:

car = {
  'type' => {
    'make' => 'Tesla',
    'model' => 'Model S'
  }
}

# normal Hash access assumes a certain structure
car['type']['make']     # => "Tesla"
car['specs']['mileage'] # => # undefined method `[]' for nil:NilClass

# Dottie lets you reach deep into a hash with no assumptions
d = Dottie(car)
d['type.make']     # => "Tesla"
d['specs.mileage'] # => nil

There’s lots more documentation on the GitHub project page. Hope this helps someone.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật