I think a good case could be made that, if you're designing a new dynamically typed language from scratch that doesn't have to support older code, safe array/object navigation should be the default.
My argument for this is you should design for the most common use case first, and most commonly people will want '.?' behavior. You can still check explicitly for null in the other cases, or even introduce a more cumbersome non-safe navigation operator. The resulting code will overall need fewer checks, be more readable, you'll have fewer crashes and exceptions, and it will generally do the right thing.
I occasionally write toy languages and this is one behavior I think I got right recently. Along the same lines, but as a more extreme measure, I would argue that invoking a method call on a null object should return null instead of throwing an exception.
I wildly disagree, because I get waking nightmares from the amount of implicit "wtf why doesn't it even enter my method" behavior that would imply, but I upvoted you because I think it's a very interesting idea and I wonder what others think.
I agree that the invokation idea is relatively wild, that's why I made it into a separate point. There are somewhat successful languages out there where sending a message to a null object doesn't do anything. If this is a class of bug that happens to you frequently, it's probably not for you. But once you get into the mindset that each method call is prepended by a virtual conditional, my hunch is you would know pretty quickly why your method isn't being called.
I already talked about my reasoning. But you are correct when you say this is a form of implicit error silencing. Dynamic languages usually have many features which could be described as implicit error silencing, and even statically typed ones often do. Implicit type conversions would be an example. You could very well argue any behavior that deviates from how the processor architecture handles data types and conditionals constitutes a silenced error of some kind. The question becomes where you personally draw the line.
Indeed I believe in Python's motto of "Errors should never pass silently. Unless explicitly silenced."
I think that Javascript's silencing of wrong parameter counts, Perl's (non-strict) allowing string concat, and non-well-founded implicit coercions (i.e: ones that change the semantics of the value) are all terribly horrible.
I believe that those who value convenience over safety deserve neither and will lose both.
> There are somewhat successful languages out there where sending a message to a null object doesn't do anything.
The reasons that is defensible in message passing systems is because such systems are basically recapitulations of computer networks, and object nullity is equivalent to a network partition. That's the kind of failure mode that you handle in the policy layer, which is above the runtime layer in an application system just as it would be above the network layer in a distributed system.
I would simply point to Apple's new language, Swift, dropping that feature as proof that they don't think it's the best way to design a language.
Our team is split (I don't know how evenly) on whether Obj-C or Swift's behavior is better. There are some staunch defenders of Obj-C's pattern being better.
I personally prefer requiring the programmer to explicitly handle nil/null. I think it improves maintainability, because future developers can see the code is correct, and it protects against changes (ex: you knew nil could never happen when your code was written, but then someone changed it so nil does occur and now your code is broken). It also saves future developers from spending time reasoning about the behavior of the code when nil occurs.
I prefer Optional to implicit nullability, but the debate isn't really between Optional and implicit nullability (is there any question there?).
The distinction is between segfault and returning 0 bits, and I'd say that returning 0 bits is generally better, because most of the time, in iOS apps, especially in absence of a "??" operator, it's what you want. For example, in a table view data source you don't have to check if your data array is non-null, just return its count, it'll be 0 if it is.
i.e. Optional monad > return all 0 bits > segfault
It's debatable whether this behavior was a good fit for Objective-C, since it's not actually a dynamically typed language. People reasonably expect their Obj-C compiler to do static checks and in this context dynamic behavior is an unwelcome surprise. It would be interesting if, for example, Smalltalk developers share the same frustration. My guess would be: probably not.
My go-to example in favor of the idea would be languages and libraries where 'null' and 'empty list' cause the same behavior in list-processing functions.
nil is a pointer to the value 0, and in C there is only one value that evaluates to a logical false, which is 0. Invoking a method on nil returns 0. This itself alleviates the kind of nil-check-chains discussed in this thread:
I'm looking forward to this in PHP7. My current workaround, which exists for the same reason as the coalesce operator (https://wiki.php.net/rfc/isset_ternary) is a custom function:
$v = @first($var1, $var2, 'some-default');
which will simply return the first parameter that has a non-null, non-empty-string value.
Here's a slightly evil way of handling this specific case in Ruby:
d = lambda {|hash,key| Hash.new(&d) }
foo = Hash.new(&d)
p foo['a']['b']['c']
The argument to "Hash.new" is the default value, or a on object that takes the hash object itself and the key, and returns the default value (and optionally stores it in the hash, which makes it great for memoization). In this case, whenever accessing a non-existent hash key, it returns another hash with the same closure as the default...
Of course it's likely to blow up in all kinds of ways since anything trying to do "if foo['a']" to check of 'a' is present in foo will break, for example.
If you don't have a legacy burden, just including and having the standard library use something like Maybe with a good comprehension syntax seems better than adopting the antipattern of using nil all over the place to start with.
I remember Groovy didn't allow ? on indexes like in the third C# example:
int? count = customers?[0]?.Orders?.Count();
I remember asking for it but it never came. I don't think Ruby's proposed .? syntax would work with indexes either unless they use [? ].
Neither Java nor Groovy has the int? declaration shown which enforces the optionality. The C# version looks more versatile.
Back when Groovy was more popular, its backers also discussed a cascading ? on their mailing list but it never happened. If C# had cascading ? then the example above would be
int? count = customers?[0].Orders.Count();
because the first ? would ripple to the subsequent member accesses and index lookups.
"try" is an active support extension on "nil", so if you're using rails (or active support), then you already have this functionality. The point being made here is that patterns developed outside the core Ruby development process are getting adopted upstream by the language for all Ruby applications to benefit from.
Also, there your example or using try falls short if the methods require additional arguments to be passed in. "try" already handles this by requiring you to "try" each method.
Of cause there is a very good argument to be had that this pattern also breaks the law of demeter by allowing you to call methods on objects that you don't own a stake in.
I agree with this. I don't see why new syntax (which looks kinda confusing, IMO) should be added to solve a problem which can be solved with a method. A naive-2-minute implementation:
module Chainable
def chain(*kl)
k = kl.shift
if self.respond_to?(k)
v = self.public_send(k)
else
v = nil
end
return v if kl.empty?
unless v.respond_to? :chain
raise ArgumentError, "can't chain on `#{k.inspect} => #{v.inspect}:#{v.class}`"
end
return v.chain(*kl)
end
end
> Because the proposed version can be made a lot faster
It being faster has nothing to do with the syntactical appearance of the operator. I think it's more pertinent to ask why does .chain_try or .tries or .fetches need a special operator than whether it's a convenient addition.
It would be trivially easy to support arguments, though. What comes to mind is to send a Hash or Array after a method (represented by a Symbol) that expects/accepts an argument:
Here `foo` and `baz` accept arguments, but `bar` does not.
Also, it shouldn't make any significant performance difference whether this was implemented as a syntactic sugar, an operator or a method (this doesn't mean it won't perform better than my example method).
At this point it's getting complex enough that it defeats the purpose. There are ways of "fixing" that (e.g. by wrapping return values and overriding method_missing or similar instead), but most of the ones I can thing of that would look nice would perform worse than yours.
> Also, it shouldn't make any significant performance difference whether this was implemented as a syntactic sugar, an operator or a method (this doesn't mean it won't perform better than my example method).
It potentially would make a massive difference. Firstly there's a minimum of 5 method calls just in the success-path in your example, plus at least one object creation (constructing a new argument array in the final call), but potentially more. And that's not nearly all.
Method calls in MRI are expensive, even with caching.
Compare that to implementing the vm-level logic which can do the actual method calls and a nil check and branch.
I still don't understand why special syntax is needed for it, though. As far as I understood your arguments, they keep pointing out flaws in my implementation (which is far from optimised). You could mimic its behaviour through C. Could you elaborate more clearly, if I'm misunderstanding something (I do want to understand).
Should be noted that Ruby on Rails has had try [1] for years, which is something that I've noticed has a tendency to be the classic band-aid for loose error handling.
Doesn't this just encourage lazy and vague programming? It sounds like it cuts down on a lot of checks in certain situations, but I'm struggling to think of a coding pattern that's actually well designed that requires so many checks at the last minute without knowing what type and data you're dealing with.
You still have to know the type, this only works for nil, so if you do "obj.?name" and the obj is not nill but doesn't have a "name" attribute, it'll blow up.
It's essentially syntax sugar to save time when you want to do something iff the whole chain is filled in.
It's in deep nesting like that that such an operator really comes into its own. If you only use it for single cases, it really isn't worth it.
In the article the 'problem' code is:
if u && u.profile && u.profile.thumbnails && u.profiles.thumbnails.large
do_something_with(u.profiles.thumbnails.large)
fi
which repeats u 5 times, and the entire u.profiles.thumbnails.large twice!
which would be replaced by only asking once.
even worse (although somewhat clearer, perhaps) is what I've occasionally seen:
if u
if u.profile
if u.profile.thumbnails
if u.profile.thumbnails.large
do_something_with(u.profile.thumbnails.large)
end
end
end
end
And even the super verbose version there still can raise exceptions when you get slightly mad data - say you decode some JSON which has u.profile.thumbnails as "null" (a string) rather than actually a null. asking
if u.profile.thumbnails
will be true - but when you request u.profile.thumbnails.large, you blow up.
You can achieve similar effects using only exceptions, but there's a good argument for using exceptions only for exceptional circumstances.
begin
do_something_with(u.profile.thumbnails.large)
rescue NoMethodError #or similar
# oh no!
end
which (alas) also will catch all noMethodErrors in do_something_with. So you then end up with
begin
tmp = u.profile.thumbnails.large
rescue
tmp = nil
end
if tmp
do_something(tmp)
end
which is super ugly.
I may have got details wrong - not being a rubyist, but I think this is the general gist of where a nestable operator makes much much cleaner code, which catches more errors.
And it's worth pointing out that the above is also slow whether you depend on exceptions or just the long if version, and depends on having methods with no side-effects, as several methods gets called multiple times.
As far as I see the proposed version will work equivalent to assigning the result of each method invocation to a temporary, and doing the next check on that, which gets far more ugly if you have to write it out
Many delegator patterns allow for "fire and forget" messaging to objects that might have not been set at runtime. This is a very useful feature/short-cut to have in a high-level language like Ruby, where the nil checks can be performed by the runtime, and not the application code.
Elvis is actually the name in Groovy for the ?: syntax (C# uses ??), not for the ?. syntax.
The name "elvis" was promoted by the then-project manager of Groovy in an attempt to change the origin meaning of "G-Strings", the name for interpolated strings in Groovy. It originally alluded to the item of clothing, but in an attempt to sanitize Groovy's image for marketing, he wanted it to have meant the string on Elvis's guitar, so he created an accompanying fun-name for the null coalescing operator.
That's fair. I come from a C# background and Mads Torgersen himself refers to the C#'s ?. syntax as the Elvis operator see (https://channel9.msdn.com/Events/Build/2015/3-711) 30:50. It's all in good fun.
> this would never pass a code review, the expectation of "foo?" is that it returns true/false.
95% of the time, yes. However, the "official" rule is simply that such methods must return a value that can be EVALUATED as true/false. There are plenty of methods in the ruby core/standard library which end in a "?" but do not return true/false, such as:
For whatever reason, Ruby allows method names ending with ? or !. AFAIK the general practice is to use ! for methods that perform a mutation in-place (e.g. list.sort! would sort a list in place, list.sort would be expected to return a new list) and to use ? for methods that return a boolean (e.g. list.empty? would indicate whether the list is empty).
Ruby inherited this from Scheme (and I prefer empty? over isempty or emptyp or variations thereof). They return truthy or falsy values and not true or false (the is* macros/functions in C only guarantee zero and nonzero as return value either).
>AFAIK the general practice is to use ! for methods that perform a mutation in-place
The ! suffix is rather loosely defined as it just means the user should be cautious/the method might have "dangerous"/surprising behavior.
Your description is usually correct but only when a non-suffixed version exists and not even then the behavior has to match your description. There are also a lot unsuffixed mutating methods. See also Kernel#exit vs. Kernel#exit! (immediate exist/exit handlers aren't called) or Tempfile#close vs. Tempfile#close! (close and unlink).
> AFAIK the general practice is to use ! for methods that perform a mutation in-place
That's not quite the usual statement of the rule: ! is for methods where there exists a "dangerous" version and a "less dangerous" version, to denote the more dangerous version. A common example of that is methods where one version returns a copy of the original object with some modification applied and the other version modifies the original object in place, but IIRCT that's not the only distinction it is used for in the standard library and, more importantly, in-place mutations that aren't paired with a "return-a-modified-copy" method don't get the !.
Only if you extend nil so it responds to "profile", "thumbnails", and "large" messages appropriately.
(Or, better, alter the method that you get the user that you are getting "u" back from to return an "nonexistent user" object that responds to "profile" with a "nonexistent profile" object that responds to "thumbnails" with a single-item list containing a "nonexistent thumbnail" object that responds to a "large" method with a "nonexistent image" (or whatever) object that has the right behavior when used where an image object is expected in the code.
This approach allows replacing more conditionals with method implementations, and is in that sense more "object oriented" on that null-coalescing which still often requires passing the result to a conditional to handle the null case, and still takes some tricks to distinguish between the terminal method returning nil and something along the chain returning nil, where that differs in the handling.
The child objects may not exist. "u" is nil (a null pointer, more or less), calling "u.profiles" sends the :profiles message to nil. NilClass probably doesn't have a :profiles method, so this will probably raise a NoMethodError exception.
As for why this is a common problem, "u.profiles.thumbnails.large" is is probably a chain of Rails/ActiveRecord model associations[1], which generally map to table rows . If there the "profiles" table doesn't have any rows "WHERE profiles.user_id = users.id", then ActiveRecord will leave a nil[2] (or empty array) in user.profiles. This is true for all foreign key relationships, so you often end up having to do something to protect against null values (no rows) every time you move between models.
Sometimes it is possible to work around this by always creating the associated model when the parent object is created, that only works in a few cases. Note: I recommend against doing tricks to auto-build the associated model on first access: that kind of "clever" solution can lead to confusing bugs later on. (I learned that one the hard way)
Thanks for the detailed answer. I think it's safe to assume that Ruby is a bit complicated at least for me but my main point was if he is just interested in checking for the `large` prop (I am not sure about the proper term here in Ruby), I'd expect him to just check for it directly and if this fails, he goes on to the next step as planned and that's why it seemed bit redundant to me esp. I didn't see any exceptions handling done to make use of the fail of previous checks on that chain to instruct the end user or dev accordingly.
Again excuse my ignorance of the subject and technical details in Ruby.
"nil" in Ruby is an instance of NilClass. NilClass has a few methods, but e.g. "thumbnail" is not amongst them, so Ruby looks for a "method_missing" method, doesn't find that either, and then throws an exception, just as if you'd called another non-existent method.
To answer a question you didn't ask, though, you can easily write classes to wrap your objects to get pretty much the effect you are asking about. E.g.:
class Maybe
attr_reader :value
def initialize value
@value = value
end
def method_missing *args
return self if @value.nil?
Maybe.new(@value.send(*args))
end
end
p Maybe.new(nil).i.can.call.anything.without.exceptions
p Maybe.new("Hello foo").gsub("foo","world").value
The first call to "p" will print a respresentation of a "Maybe" instance holding "nil" in "@value".
The second will print "Hello world", as "gsub" will be called on "Hello foo" since the "Maybe" has a non-nil value.
There are any number of implementations of helper classes like this for Ruby, with various levels of popularity. The benefit of course is that you can easily customize the behaviour for your specific needs (e.g. you could create one that will return a default value instead of nil, or you can create one that short-circuits on other values than nil, or any number of alternatives)
The advantage of introducing .? as proposed, though, is that it is easier to optimize (no new class or lots of extra objects to create and destroy).
So, "nil" is not a primitive value in Ruby explains the extra care taken with these control flow statements in the language.
Let's get back to the example, let's say that checking for «u» would return «nil», wouldn't checking for «u.profiles» would result in an exception thrown nonetheless?
Almost nothing is a primitive value in Ruby. Not even true/false/numbers are "traditional" primitive values.
> Let's get back to the example, let's say that checking for «u» would return «nil», wouldn't checking for «u.profiles» would result in an exception thrown nonetheless?
No, because `u.?profile` would return `nil`, then, when you try `nil.?thumbnail` on nil again, it would return nil. So, doing the following, with `u` being `nil` is legal:
if u.?profile.?thumbnail
# doesn't run if u is nil
end
Everything is an object in Ruby. E.g. you can do "42.to_s(16)" to convert 42 to base 16.
That includes nil, true, false (instances of NilClass, TrueClass and FalseClass respectively).
A common way of explicitly checking for nil, as opposed to nil or false is to do "foo.nil?", for example, which explicitly calls the method nil? on foo, which will usually return false for objects other than nil (nothing stops you from overriding it if you want your class to pretend to be nil - e.g. maybe I'd want my "Maybe" class to return true for "nil?" if the value it holds is nil).
As for the example the whole point is that like my naive "Maybe" implementation, this new operator would check if the target is nil before trying to call a method. Unlike my naive implementation, it doesn't need to wrap objects, and it can short-circuit evaluation if it meets a nil (you can implement versions that will short-circuit evaluation too, but I can't see any obvious ways of both letting you short-circuit and getting "clean" syntax if you want to support method arguments).
If the default behaviour of nil on a send for a method that doesn't exist was to return nil, then nils will flow through your program further than anticipated and debugging will become more difficult.
Nils will get into data structures and figuring out when and where they got there will become more work. You'd end up writing a lot more logic testing for nil and throwing an exception when it's found, to avoid the nil propagating further.
While it's probably a good idea to treat nil as a null pointer that doesn't do anything, it's still an an instance of NilClass and can be used as an object.
>> class NilClass
• def foo!
• puts "bar?"
• end
• end
=> :foo!
>> nil.foo!
bar?
Rails uses[1] this with the very-useful #blank? method.
It's arguable whether NilClass#blank? is very-useful (the same can be said for #to_a, #to_c, #to_f, #to_h, #to_i, #to_r, and #to_s).
I'd call it marginally convenient, because you have to type less, but it doesn't increase usability, and the readability is pretty much the same (or worse, because someone would have to know NilClass responds to blank?, if they don't they might've expected an empty array, or an empty string).
Also, why does FalseClass respond to blank? with `true`, but not TrueClass (I didn't know it did this for those two until now)? How is false less blank than true?
NilClaass#blank? is incredibly useful because it compliments String#blank? and Array#blank?, which are mostly[1] an alias of #empty?. For most uses where you already know you have a string or array, you probably want to use #empty? directly.
The entire point behind #blank? is that it provides a cross-class[2] method that works even when the object doesn't exist (and you get nil instead). This was designed for Rails to simplify situations such as the params[3] (parsed URL query string), which might not have a particular value.
# when handling "/action?foo=bar"
params[:foo] # => "bar"
# when handling "/action?foo="
params[:foo] # => ""
# when handling "/action"
params[:foo] # => nil
With #blank?, you only need one function call to handle both the empty string and nil cases.
if params[:foo].blank?
# render a static placeholder (never looks at params)
else
# render Foo's markup, using the value in params[:foo]
end
If you cared about the empty states other than as a generic "it's blank", other methods may be more appropriate.
> why does FalseClass respond to blank? with `true`, but not TrueClass
That's a good question. I would have assumed both true and false as not-blank (#blank? => false), as those could both be entered (#present?) values. I suspect it's related to HTML forms only including successful controls[4] in the query string.
I am aware of where it's used, and I am aware it can be convenient, although I am not very enthusiastic about using it.
> I suspect it's related to HTML forms only including successful controls
Yeah, this is a good point, since an unchecked checkbox isn't transmitted in a standard HTML form, but Rails does add a hidden input element with the value false; so I guess #blank? does somewhat increase readability here, but this could've and should've been fixed in a better way (a few ideas come to mind).
Don't get me wrong, I probably used #blank? (and might in the future) just for the sake of convenience. However, it makes me feel guilty when I do, because it could subtly introduce and hide bugs due to lack of more adequate checks.
My argument for this is you should design for the most common use case first, and most commonly people will want '.?' behavior. You can still check explicitly for null in the other cases, or even introduce a more cumbersome non-safe navigation operator. The resulting code will overall need fewer checks, be more readable, you'll have fewer crashes and exceptions, and it will generally do the right thing.
I occasionally write toy languages and this is one behavior I think I got right recently. Along the same lines, but as a more extreme measure, I would argue that invoking a method call on a null object should return null instead of throwing an exception.