4 ms·
I actually like and use the last one. I don't see why it falls into the "don't do this" category of tricks. I consider def foo(bar=(default_given = true; ni
by Xylakant 14y ago
I actually like and use the last one. I don't see why it falls into the "don't do this" category of tricks.
I consider
def foo(bar=(default_given = true; nil)
puts "default value given" if default_given
end
a quite readable solution to the problem. Actually, it's the only solution if you need to know if an explicit value was passed and you don't have any restriction on what values are acceptable.
- shokwave 14y agoAgreed, instance_eval is definitely disgusting, but this trick scores well on readability, it's no more of a trap than some other Ruby idioms, and it serves a good purpose (robustness!). def foo(bar, baz=(default = true; 'default')) # it still looks separated from other arguments if default puts "#{bar}.times { puts #{baz} }" else bar.times { puts baz } end end To anyone who knows more Ruby than I - is there a good reason against that I'm missing?
- josephlord 14y ago> To anyone who knows more Ruby than I - is there a good reason against that I'm missing? I probably know less Ruby than you but these are my thoughts. 1) I took me a while to work out what was happening but I finally figured out that the default value is only evaluated when the argument isn't present. 2) If the default value is indicated in the docs it would be wrong for the function to behave differently depending on whether that default was relied on explicitly sent. If you need to know this you are probably doing something wrong elsewhere.
- caiusdurling 14y agoI think I've only ever used it once, and that was in a place where refactoring my way around needing to know that was more trouble than it was worth at that point. I label it as evil, because people I've shown it to range from "that's amazing, I'd never allow it in production code" to flat out "that can't work". So it's not a common idiom in my experience, nor do people usually grok it on first pass so to me it fails "write unsurprising code" as someone who is part of a team. Each to their own of course, the "evil" positioning of the post was a bit of sarcasm to me really. I would use those techniques in situations appropriate to them. For the most part, I'll avoid them though, especially in code that I know won't be a simple one-off script.
- Xylakant 14y agoI agree that this trick is a bit hard to really understand if you first see it the first time and even harder to come up with, but I think it's quite readable even if you don't understand what exactly is going on under the hood. So I'd have no qualms about using or allowing it in production code. It's safer than any proposed alternative, readable and does the job. Whether it's actually good architecture to depend on that knowledge is another discussion, but there's cases where it's required (see the inject example in this thread) I'd never allow the instance_eval trick though, because that obfuscates the code for sake of saving a local variable.
- damncabbage 14y agoI'm still trying to think of a situation where I want to know if someone has passed the default value or not. The method itself shouldn't care; it returns the same result regardless whether an argument was passed explicitly or not. What am I missing? :(
- caiusdurling 14y agoThe one time I've used it seriously was a method that was given the returned body of a HTTP GET call. If I remember rightly it was automatically parsed if it was a JSON body, so you could quite rightly get a nil body back, and that wasn't considered an error. However, if the body failed to parse, it invoked this method without an argument (bad decision further up the call stack, but by the point I hit it I didn't have time to refactor around it right then. AFAIK it was changed later to be less shit code and do stuff properly.) So I wanted to know in that method if nil was passed in by argument (as the parsed body was nil), or if no argument was passed, at which point the argument was set to nil as the default value by ruby. Having ruby set a `default = true` if no argument was passed was pretty much the only way to get myself out of that hole right then. Of course, having been coded into that corner once, I'd never do it that way again, so I'm not sure I'll ever be in a corner I need to use it to get out of again. Still useful to have the technique in the back pocket though, just in case!
- Mon_Ouie 14y agoMethods that simply behave differently depending on whether or not an argument is given. For example, passing nil (or any other object) to inject is different from passing nothing. [1, 2, 3].inject(:+) [1, 2, 3].inject(0, :+) [1, 2, 3].inject(nil, :+) # doesn't work Rubinius uses a magic ``undefined`` value for this: def inject(initial=undefined, sym=undefined, &block)
- philh 14y agoYou might want a lookup method to usually throw an exception if it doesn't find anything, but sometimes return a user-specified default. It's problematic though, because writing a thin wrapper becomes a pain in the ass.
- judofyr 14y agoYou can also do: NULL = Object.new.freeze def foo(bar = NULL) if bar.equal?(NULL) # default value end end
- Xylakant 14y agoright, that would work to. I'd still prefer the original solution since it avoids creating a magic constant.
- bradleyland 14y agoThis one is a matter of perspective. It's really not all that difficult to parse, but it feels icky depending on how you view the scope of "appropriate" behavior in method argument definitions. Like many viewpoints, the "wrongness" of this example is not black & white; it's shades of gray. On one hand, you have the "anything that will eval is valid Ruby" view, and on the other you have the "If it's not immediately obvious to a beginner, you shouldn't do it" view. There may be better ways to express those two sides of the matter, but that's the general idea. The problem with this code (from the latter viewpoint) is that it crams too much program logic in to the argument definitions. This example uses parenthesis to force the evaluation of default_given = true; nil` in the argument definition list. That's only two statements, but it violates some common expectations: A) We generally expect argument definitions to be clear and readable, so that method definitions are self documenting (to some degree); this approach clutters the argument definitions B) We expect argument definitions to sometimes assign default values C) We expect program logic to appear in the body of a method, or to be DRY'd up in separate methods In this way, the example is not "incorrect" but awkward. To borrow an idea from the literate programming camp, I'd say that just because you can write awkward sentences with valid grammar, it doesn't mean you should.