4 ms·
Why not just define a function in your code, rather than redefining 'nil?'?
by pdubroy 18y ago
Why not just define a function in your code, rather than redefining 'nil?'?
- jfarmer 18y agoBecause you want to be able to pass it to functions that expect Hash semantics? Yay weak typing! cough
- dasil003 18y agoIt's a kludge so that people don't have to write: if hash[:a][:long][:key][:chain].is_a?(SafeNestedHash::UndefinedHash) Instead they can just say: if hash[:a][:long][:key][:chain].nil? If the nested hash IS defined it could be any type under the sun, therefore a method has to be defined on object, and nil? seemed the only reasonable choice.
- tdavis 18y agoMaybe I don't really understand Ruby, but isn't it basically a duck-typed language? Why would you be checking the type at all (which I am assuming `is_a?` does)? Type checking is evil for weakly-typed languages. Either it implements what you need it to implement or has some property value, otherwise it should throw an exception. What are you testing for here? To see if the hash is empty or something? What makes an "UndefinedHash" undefined? If it IS defined, then it should be assumed to be the type of object you are looking for until the code tries to do something that breaks on a non-supported type.
- dasil003 18y agoThe purpose of the SafeNestedHash is to be able to directly reference or assign deep nested hashes without initializing every level. Ruby will throw an error if you do this with a regular hash because the first undefined level returns nil and nil does not respond to the [] method (square brackets are just a method in Ruby). The UndefinedHash is a class that stands in for nil so we don't get an error as we make a deep reference and it also keeps track of the keys so that if there is an assignment at the end it can initialize the necessary sub-hash and attach it to the original top-level hash. There are simpler hacks around that auto-initialize a hash, but they suffer from auto-vivification of hash keys (ie. looking up a hash key will create an empty hash there even without an assignment). In essence, the problem could be "solved" by monkey patching nil, though that would be the worst sort of abuse of monkey patching I can imagine. As far as checking the class is concerned, well that's just a convenient method of showing what I'm doing, I could also define a method such as is_not_defined? and check if the object at the end of the hash responds to that. The fundamental issue is that any object could be stored in the hash, so whatever I check has to be valid for any possible object. That's why I defined nil? for the UndefinedHash class even though it is a bit kludgey--it's better than monkey patching Object or NilClass.
- tdavis 18y agoRuby will throw an error if you do this with a regular hash So, why not just let it throw an error? The interpreter is expecting one thing and gets another -- that's a type error right there. Why bother with anything else? (ie. looking up a hash key will create an empty hash there even without an assignment) That's pretty silly ;)
- dasil003 18y agoNow you're asking for the entire justification of my class to exist. Say there is an algorithm you want to run where you are grouping things into a deeply nested set of categories. A nested hash referencing arrays is a good data structure for this. In PHP this is trivial (though not terribly efficient). In Ruby you have to deal with maintaining the hash. There are many ways to do it, but my SafeNestedHash class lets you do it with a concise, natural syntax. Perhaps if you have an aversion to using a library for this then you would define a pair of methods like key_exists?(hash, key_chain) and set_key(hash, key_chain). You could also monkey-patch hash to have those methods. You could also encapsulate the logic into an object that handles all the logic. Or (as you seem to suggest) you could let the interpreter throw an exception and then handle it with rescue, which IMO would be just a notch behind monkey-patching in terms of bad practice since exceptions should be for unexpected circumstances. I just happened to go with creating a class that abstracts this all away. Yes there is a bit of leakiness, but there is no monkey patching, and any other solution would require either ballooning the actual function with incidental accounting details, or else utilizing some other abstraction which would need to be used with the same understanding of its purpose, at which point it's just a matter of taste.
- tdavis 18y agoI think you're misinterpreting exceptions in so much that you feel they need to be unexpected. Say you have a list like [[1,2,3],[4,5,6]]. You could count each of the lists and loop through them individually, or you could create a recursive function that assumes a flat list and switches when an index error occurs: def wtf(l, count=0): cl = l[0] try: return wtf(cl[count:], count+1) except IndexError: # Reverse the list and start over cl = l[::-1] return wtf(cl, 0) Hopefully that gets my point across. Basically, exceptions are very useful, and there is nothing wrong with expecting and catching their throws under certain circumstances.