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.
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.<p>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:<p><pre><code> def admin_of?(project)
membership_for(project).admin? || false
end
</code></pre>
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.)<p><pre><code> def admin_of?(project)
membership_for(project).admin?
end</code></pre>
When I first looked at Haskell, the 'Maybe' concept was new to me and seemed like a useful concept for other languages. <a href="http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-Maybe.html" rel="nofollow">http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0...</a>
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:<p><pre><code> =io.open 'sdfasdf'
nil sdfasdf: No such file or directory 2
</code></pre>
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:<p><pre><code> f=io.open 'sasdasdfsf'
if f then -- nil is a boolean 0
</code></pre>
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:<p><pre><code> f, err = io.open 'sasdasd'
print(err)
</code></pre>
I believe this also the design decision Google's Go language has made: <a href="http://golang.org/doc/effective_go.html#multiple-returns" rel="nofollow">http://golang.org/doc/effective_go.html#multiple-returns</a>
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.
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.
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.<p>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:<p><pre><code> (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.
</code></pre>
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_:<p><pre><code> 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.
</code></pre>
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?
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).<p>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.<p>I could see this sort of proxy object mechanism being extended so that you could add retrying mechanisms to the code.
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.<p>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.
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.<p>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'.