5 ms·
I don't understand why this needs a special syntax -- to me ruby already has a little too much syntactic variation. Why not just extend fetch or try e.g. -- .t
by forgotpwtomain 11y ago
I don't understand why this needs a special syntax -- to me ruby already has a little too much syntactic variation.
Why not just extend fetch or try e.g. -- .try(:profile, :thumbnails, :large)
- maniacalrobot 11y ago"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.
- hiharryhere 11y agoFYI .try takes a block. Passing a symbol is just shorthand, similar to how map(&:method) is shorthand for .map {|x| x.method } So technically you can pass arguments to a try method by doing object.try {|o| o.method(args) } Not trying to be pedantic, hopefully being helpful.
- maniacalrobot 11y agoEveryday is a learning day!
- spoiler 11y agoI 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 and then this: class Object include Chainable end
- vidarh 11y agoBecause the proposed version can be made a lot faster, even before considering that your version doesn't handle method arguments
- forgotpwtomain 11y ago> 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.
- vidarh 11y agoIt doesn't need a special operator for other reason than that by specifying the VM-level behaviour it can be made a lot more efficient. And at that point naming it after something that are legal method names would be problematic.
- spoiler 11y agoIt 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: o.chain :foo, [1, 2, 3], :bar, :baz, {key: 'value'} 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).
- vidarh 11y agoAt 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.