I could write these clients as lambdas or nested functions which close over the stuff which init creates. But why? An object makes it much clearer that there is state.
I could write these clients as lambdas or nested functions which close over the stuff which init creates. But why? An object makes it much clearer that there is state.
In languages that let you operator overload function call syntax, you end up with an object that, once constructed, supports being called like with same syntax as a function call. This works easily in python (define __init__ and __call__ ), and you don't have to fight the typesystem to structure code that will accept both a callable object or a function.
Another perspective of the whole thing is that you have a function with many arguments, then you curry to bind some arguments, then pass the resulting function with remaining free arguments to be called.
I prefer structuring code as functor objects as it lets you access the bound parameters as attributes (if you want to expose them) which can also sometimes be useful in code or in test
> Another perspective of the whole thing is that you have a function with many arguments, then you curry to bind some arguments, then pass the resulting function with remaining free arguments to be called.
It's not the same, though, because a socket etc is being constructed in the constructor. Here's an abridged (and possibly wrong!) version of a monitoring client:
class Monitor:
def __init__(self, monitoring_url, app_name):
self.monitoring_url = monitoring_url
self.session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=1))
self.details = ujson.dumps({'app_name': app_name, 'pid': os.getpid()})
async def send(self):
async with self.session.post(monitoring_url, data=self.details, raise_for_status=True) as resp:
pass
If you treat that as currying, you will create a new ClientSession every time you call ping(). A ClientSession contains a connection pool, so that means you will create a new socket instead of reusing one.> In languages that let you operator overload function call syntax, you end up with an object that, once constructed, supports being called like with same syntax as a function call. This works easily in python (define __init__ and __call__ ), and you don't have to fight the typesystem to structure code that will accept both a callable object or a function.
In Python and Java, you can easily refer to bound methods to produce callables from objects, so this seems like unnecessary work.
After that, it's a question of taste. Agreed, callable objects can be useful but if you don't modify your "self" during function calls, partial funciton aplication might be clearer.
But the parent comment applies
def __init__(self):
pass
Not necessarily. Or the constructor is just setting some values that are later used by the calculation but are not modified and used later var quadrupler = new ConstantMultiplier(4);
var output = quadrupler.applyTo(42);
The 'quadrupler' instance is stateless in that it's immutable, but it presumably has a (constant) member to store the value 4.For example, I recently wrote this:
const labelizeElement = ((element) => element.type.replace(/\s+/g, ''));
const labelizeRelation = ((relation) => relation.type.toUpperCase().replace(/\s+/g, '_'));
const groupByLabels = (labelizer) => (set, element) => (
set[labelizer(element)]
? { ...set, [labelizer(element)]: [element, ...set[labelizer(element)]] }
: { ...set, [labelizer(element)]: [element] }
);
const groupByElement = groupByLabels(labelizeElement);
const groupByRelation = groupByLabels(labelizeRelation);
I needed two functions that did almost the same thing, but not quite. I don't want to write a ton of nearly-duplicate code, so instead I extract the bits that are different, and write a function that I actually need.So yes, I'm inclined to say that only mutable state needs a class. And the more you get used to working with constants and immutables, the less mutable state you're going to find.
I used to be a big fan of OO, but somehow experience seems to have landed me in the functional programming camp.
const labelizeElement = ...
const labelizeRelation = ...
const groupByLabels = (labelizer, set, element) => ....
const groupByElement = (set, element) => groupByLabels(labelizeElement, set, element);
const groupByRelation = (set, element) => groupByLabels(labelizeRelation, set, element);
This makes it slightly simpler to create a binding function around groupByLabels which fixes, say, the middle argument. I also think that avoiding the chained use of the => operator makes the code more readable to the average JavaScript dev.C++ has (strange looking) support for the bind pattern in its standard-library now [0], and Python3 has functools.partial but it only supports left-to-right binding [1] (not that there's any particular reason to avoid lambdas).
[0] https://en.cppreference.com/w/cpp/utility/functional/bind
This is my key question, and I ask with minimal assumption that either one is better.
What are the criteria we are even using to judge? Clarity that there is state is one. Elsewhere there are arguments about ABI that I fail to have practical knowledge of. There was some discussion of the data structure describing your program that completely lost me. Explicitness is lost when discussing closures.
What makes one better than the other in ways that arent entirely subjective?