I'm going through a phase of trying to avoid temporary variables and over-use of conditional where I can use a more fluid style of coding. I've taken a great liking to using #tap
in places where I want to get the value I need to return, but do something with it before I return it.
def fluid_method
something_complicated(a, b, c).tap do |obj|
obj.update(:x => y)
end
end
Vs. the procedural:
def non_fluid_method
obj = something_complicated(a, b, c)
obj.update(:x => y)
obj # <= I don't like this, if it's avoidable
end
Obviously the above examples are simple, but this is a pretty common coding style in the ruby community nonetheless. I'll sometimes use #inject
to pass an object through a series of filters too:
things.inject(whatever) do |obj, thing|
thing.filter(obj)
end
Vs. the procedural:
obj = whatever
things.each do |thing|
obj = thing.filter(obj)
end
obj
Now I'm facing repeated use of a condition like the following, and looking for a more fluid approach to handling it:
def not_nice_method
obj = something_complex(a, b, c)
if a_predicate_check?
obj.one_more_method_call
else
obj
end
end
The (slightly) cleaner solution is to avoid the temporary variable at the cost of duplication:
def not_nice_method
if a_predicate_check?
something_complex(a, b, c).one_more_method_call
else
something_complex(a, b, c)
end
end
I can't help but feeling the desire to use something almost like #tap
here though.
What other patterns might I follow here. I realise this is all just nonsensical sugar to some people and that I should just move onto more interesting problems, but I'm trying to learn to write in a more functional style, so I'm just curious what long-term rubyists have determined to be good ways to tackle situations like this. These examples are hugely simplified.
#update
would return a boolean, not the value ofobj
(which is beyond my control), doesn't tap solve the need for a third expression to return the original value? I would like to understand more correct functional techniques :) – Upsweepupdate(something_complex(a, b, c))
, where I have definedupdate
to doargument.update(:x => y)
... though this gets more verbose as the update parameters need passing in. – Upsweep3.tweak { |i| i * 2 } # returns 6
which is equivalent tolambda { 3 * 2 }.call
. It would also let you create and use references mid-chain:5.tweak { |id| obj = expensive_lookup(id); obj.ready? && obj.valid? }
which is equiv tolambda { obj = lookup(id); obj.ready? && obj.valid? }.call
. – Krys