

If you gaze into nil, nil gazes also into you - helium
http://robots.thoughtbot.com/post/8181879506/if-you-gaze-into-nil-nil-gazes-also-into-you

======
tptacek
This is very consultant-y code. It's great billable time to have another
reason to take 1 line of code with a predicate and "refactor" it into a
broccoli floret of mock classes and exception handlers. But really, nothing
insulates a competent programmer from needing to know what a library is going
to return.

~~~
jakehow
Exactly, they have traded .nil? for .present? and added complexity.

------
SeoxyS
I think this is mostly a workaround for a huge flaw in most
languages—especially one as dynamic as ruby. Nil should accept any method and
return nil.

This is the pattern that Objective-C embraces, and it works very well. You can
chain method calls to nil and get nil as the result of the expression, should
any method return nil. You end up with cleaner, more readable code. Sure, it
makes some edge cases harder to debug, but not by very much. In this case, the
benefits far outweigh the code. You'd simply end up with:

    
    
      def admin_of?(project)
        membership_for(project).admin? || false
      end
    

Additionally since nil is falsy, you could even skip the `|| false`, and any
`if u.admin_of? p` statement would still work. (Not recommended, but just
pointing it out.)

    
    
      def admin_of?(project)
        membership_for(project).admin?
      end

~~~
DanielRibeiro
In Ruby nil can receive any method and return nil:

    
    
      class NilClass; def method_missing(*args); nil; end; end
    

The problem? It makes code fail late and siltently, which can happen with a
simple mispeling of key names on hashes.

In ruby, Andand allows you to solve this more locally:
<http://andand.rubyforge.org/>

~~~
klochner
andand doesn't seem any different than using try(:method), which is defined in
Rails 2.3+ for the Object class:

    
    
      > nil.try(:name)                     #==> nil
      > User.first.try(:name)              #==>"john doe"
      > nil.try(:name).try(:upcase)        #==> nil
      > User.first.try(:name).try(:upcase) #==>"JOHN DOE"

~~~
DanielRibeiro
Thanks for pointing out the try method (it is important to note that it is
avaible to anyone using Active Support, not only Rails apps).

It is mildly different (syntax mostly). All of these are attempts to recreate
the elvis operator tha Groovy[1] (nowadays present as the The Existential
Operator in Coffescript[2]) have.

For better chaining, the Maybe monad can be implemented in Ruby[3]. However
they really shine on Haskell and Scala because they have, respectively, do
notation and for comprehensions, which effectively work (on this restricted
case of handling nulls) as a scope were nulls are ignored.

Doing this with ruby is possible, but requires AST metaprogramming[4], which
is the sort of thing that LISP macros do (and is quite suitable to accomplish
in LISP[6], as it is homoiconic[5], while Ruby isn't).

[1] [http://groovy.codehaus.org/Operators#Operators-
ElvisOperator...](http://groovy.codehaus.org/Operators#Operators-
ElvisOperator%28%3F%3A%29)

[2] <http://jashkenas.github.com/coffee-script/#operators>

[3] [http://pretheory.wordpress.com/2008/02/14/the-maybe-monad-
in...](http://pretheory.wordpress.com/2008/02/14/the-maybe-monad-in-ruby/)

[4]
[http://metaphysicaldeveloper.wordpress.com/2010/10/31/rubyun...](http://metaphysicaldeveloper.wordpress.com/2010/10/31/rubyunderscore-
a-bit-of-arc-and-scala-in-ruby/)

[5] <http://en.wikipedia.org/wiki/Homoiconicity>

[6] [http://onclojure.com/2009/03/06/a-monad-tutorial-for-
clojure...](http://onclojure.com/2009/03/06/a-monad-tutorial-for-clojure-
programmers-part-2/)

~~~
klochner
Thanks for the links. Some ruby food for thought . . .

    
    
      > x = nil || 5                   #behaviorally equivalent to elvis?
      > [nil,4,3].compact.map(&:to_s)  #null-ignored scope

~~~
DanielRibeiro
You are welcome. The second line is the essential notion of Maybe monad as a
restriction of the Sequence monad (Some of x works like [x] and None works
like [], using ruby's [] notation for lists).

------
jz
When I first looked at Haskell, the 'Maybe' concept was new to me and seemed
like a useful concept for other languages.
[http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0...](http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-
Maybe.html)

~~~
mechanical_fish
Haskell is compiled. As I understand it, much of the point of Maybe is that it
provides a formal language for expressing and manipulating unhandled edge
cases. Then the compiler can spot those unhandled edge cases at compile time
and make sure you handle them at one level of scope or another.

In Ruby the concept seems less useful because there is no compiler.

~~~
leif
That's not the point, the point is that Maybe is the right way to handle what
nil (or None or null) fails to handle properly. Scala gets it right too, IIRC,
with Nullable.

1\. A "null" instance of one type should not be conflated with a "null"
instance of a separate type. 2\. By type-wrapping in a Maybe, you declare
where you need to be able to handle nulls and where you are free to ignore
them (but can never pass them in). You confine the null to specific regions of
your code. 3\. You force your code's clients to think about the null case
wherever you make it visible.

~~~
Locke1689
Maybe is really just a special case of Either, where it optimizes for the
situation where the second return type is "failure." The type of Either is
necessary because proper type theory requires a container type to wrap two
types. This is immediately obvious if you try to write the type signature for
any function which has a return arity of one.

------
davidhollander
I'm a big fan of Lua's use of nil and the simplified error handling (No
exception 'types') in that language. I think the problem with 'typing'
Exceptions is that it means you are using it to record and pass
describable\known state similar to return, instead of being reserved for
unknown state. For indicating predictable failure states without using
exceptions, I think multiple return values is a much better paradigm. Here's
what happens when you open a non-existent file in Lua:

    
    
        =io.open 'sdfasdf'
        nil	sdfasdf: No such file or directory	2
    

The first value returned is nil, the 2nd value is a string containing an error
message, and the 3rd is an integer error code. This allows you to write most
code using 2 state boolean logic:

    
    
        f=io.open 'sasdasdfsf'
        if f then -- nil is a boolean 0
    

whereas in Python, one often has to reason about 3 states by using the
try\except blocks. Even though you are not using typed Exceptions, multiple
return values does not discard any state about the error if it is desired:

    
    
        f, err = io.open 'sasdasd'
        print(err)
    

I believe this also the design decision Google's Go language has made:
<http://golang.org/doc/effective_go.html#multiple-returns>

~~~
LeafStorm
This also works really well when combined with the assert function. Any
function that returns nil and an error on failure can be wrapped in assert
(which is just a normal function that takes a value and a message, and raises
an error with the message if the value is nil or false).

If the function returned a value (besides nil or false), then assert merely
returns the value. If the function returns nil or false, then assert raises an
error, and the error message is used as the error in the assert. For example:

    
    
      > = assert(io.open("hello"))
      file (0x8b9f658)
      > = assert(io.open("goodbye"))
      stdin:1: goodbye: No such file or directory
      stack traceback:
        [C]: in function 'assert'
        stdin:1: in main chunk
        [C]: ?
    

This makes it possible to fail early when you don't need or want to do full
error handling, or to use advanced logic if you need to recover from the error
- without a try/catch statement. Effectively, it makes exceptions optional.

------
jsankey
This argument just seems like a step along the path to static typing. If
you're using Ruby, haven't you already decided that you prefer the simplicity
of a dynamic language to the safety of static types? So why start to add half-
baked typing to your code? It seems like it just puts you in an uncomfortable
middle ground.

------
leif
I commend the author for re-implementing basic typing for Ruby, but this
example completely misses the point. If the client to User asks whether a user
is an admin of a project before asking if it is a member of that project, the
client code is broken already.

------
asymptotic
I can't comment on Ruby, but as the author decided to throw in a
"...AttributeError in Python" reference I figure I have two cents to add.

Unfortunately the author is out of their depth. Why are they checking for
specific attributes or assuming objects are an instance of a particular class?
This violates the spirit of Python. Indeed, from the Python glossary:

    
    
        (Duck typing is a) Pythonic programming style that determines an object's
        type by inspection of its method or attribute signature rather than by
        explicit relationship to some type object ("If it looks like a duck and
        quacks like a duck, it must be a duck.") By emphasizing interfaces rather
        than specific types, well-designed code improves its flexibility by
        allowing polymorphic substitution. Duck-typing avoids tests using type()
        or isinstance(). Instead, it typically employs the EAFP (Easier to Ask
        Forgiveness than Permission) style of programming.
    

The author even misinterprets the first reference they provide! How outrageous
is that? The author links to a blog comment as "These errors are one of the
largest sources of bugs.", but the actual link _explicitly states_:

    
    
        The nowhere-near-ready-for-peer-review numbers I've seen suggest that
        something like 70% of bugs in Java manifest to the programmer as
        NullPointerExceptions.
    

These bugs _manifest_ using some language-specific exception, but clearly the
actual bug is a different kettle of fish. It could be absolutely anything;
poorly specified interfaces, well-specified interfaces that are called badly,
some lower-level exception getting silently caught, inconsistent state. What
does null, nil, None, NULL, whatever, have to do with this?

------
daemin
I think one's opinion of the final solution in the article boils down to if
you would prefer to raise (or throw) an exception if there desired value is
not found (i.e. like in Java when a file is not found), or you would rather
return an error value (i.e. NULL in C from fopen etc).

This is just creating a specific proxy object to return a more specialised
error condition rather than just returning nil and making the user of the
function guess at what could have gone wrong.

I could see this sort of proxy object mechanism being extended so that you
could add retrying mechanisms to the code.

------
ataggart
Part of the problem stems from the calling semantics of the language, namely
when the operation hangs off of an object. Broadly speaking, functional
languages avoid that manifestation.

The other part of the problem stems from handling nils when one is given them,
either as the result of an operation, or the arguments to one. Either way,
this necessarily needs to be a context-specific decision.

------
yxhuvud
Having read the comments, I havn't seen what I find to be the gravest problem
with this article - the poor OO practices of the author.

Seriously. If you have a family of methods on a user that only work in the
presence of an instance of a project, then the first thing you ask yourself
should be 'hey, maybe these methods belong on the project instead' and not
'hey, lets reinvent nil'.

~~~
hakunin
Don't these methods only work in the presence of an instance of a user as
well?

