Hacker News new | past | comments | ask | show | jobs | submit login

The example from the blog post is a classic error we've all made. There seems to be an obvious opportunity for Rails do something creative about that.

I like the String? sugar and believe there is probably some opportunity in Rails to address it. For example make a new NillableObject class that wraps the real object or delegates method_missing.

The idea would be to throw a warning, raise an error, etc. when accessing a nillable attribute. This could get obviously more robust than the below example, hook into read_attribute, leverage validations, log a warning instead of raise a runtime error, etc... but hopefully the point is made.

  class NillableObject
    def initialize(obj = nil);@obj = obj;end

    def method_missing(name, *args, &block)
      raise "I'm nillable, don't call methods against me directly"
    end

    def try(&block)
      return if @obj.nil?
      block.call(@obj)
    end
  end

  ns = NillableObject.new
  ns.gsub("f","g") # RuntimeError: I'm nillable, don't call methods against me directly
  ns.try{ |o| o.gsub("f","g") } # => nil

  s = NillableObject.new("food")
  s.gsub("f","g") # RuntimeError: I'm nillable, don't call methods against me directly
  s.try{ |o| o.gsub("f","g") } # => "good"



This totally makes sense. I think the disadvantage is that this is done at runtime, but assuming you have a test that hits it, it would catch the bug.

My colleagues wrote a library that does something like this. You may want to check it out: https://github.com/thoughtbot/wrapped


Nice, looks well thought out.

Yeah the runtime thing is a disadvantage over type systems for sure. It might be possible with a NillablObject to do some kind of sanity checks at Rails initialization time. Also some other safety nets could help for example use of a ViewModel object to dictate some constraints on Views (type constraints and others).

I like Lucky’s table definition in the model class. A similar construct in AR might be useful in implementing some sanity checks. I often wish there was a way to get a table definition into AR while keeping the goodness of Migrations.


I wonder if the culture of "Optional" with ".and_then" / ".or_else" has any foothold in the Ruby or Crystal communities. Functional approach is so much simpler and more elegant; with Ruby's blocks, it can be made to also look natural.

With promises and things like array.map being widely accepted in e.g. JS community, I'd hazard to say that mainstream industrial programming finally starts to embrace the use of monads. It would be great to embrace the most badly missing of them all, Option / Maybe, instead of the "billion dollar mistake" of null. ("Nullable" is a half-step in the right direction.)


I believe that while monads are great, crystal obviates the need for option because String? doesn't mean "nullable string", it means (String | Nil) or "the type which is the union of String and Nil". This is a much more powerful and generic concept than nillable types, as you can call any method in this object which is defined both on String and Nil. One such method (which happens to be defined in every type) is the method try. This is equivalent to a map in monad optional. I'm sure you can see that this generalises to make type unions possible to represent the optional type (with no overhead) and all methods which you could implement on it. Furthermore, its actually more powerful as this is implemented at the type system level, meaning flow typing works, which makes code much cleaner as you can use normal if statements with boolean conjugates to "unwrap" these types (really just removing types from the union) which is cleaner.


This (chaining "try" calls) is basically what I had hoped would happen :)

It is the "happy path", represented compactly but without a way to forget and step on a nil.


Ruby has new syntax to handle calling methods on null objects.

obj&.is&.nil? can replace the use of try obj.try(:is).try(:nil?)


Agree, love this syntax

  obj = nil
  obj&.gsub("f","g") # => nil 
  obj = "food"
  obj&.gsub("f","g") # => "good" 
Unfortunately this is not likely second nature and will continue to bite us, especially where our expectations about AR models are not well thought out. A wrapping class for nillables might force safe access of AR attributes.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: