This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Signature binary function (e.g. comparison) design #50
Comments
About the question: "Are there things we cannot do with key functions that we can with the usual binary operator way (specifying comparison function via a two-element boolean function such as compare(x, y))?", the answer should be "No" because of the existence of |
The code of cmp_to_key is nothing more than: def cmp_to_key(mycmp):
"""Convert a cmp= function into a key= function"""
class K(object):
__slots__ = ['obj']
def __init__(self, obj):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) < 0
def __gt__(self, other):
return mycmp(self.obj, other.obj) > 0
def __eq__(self, other):
return mycmp(self.obj, other.obj) == 0
def __le__(self, other):
return mycmp(self.obj, other.obj) <= 0
def __ge__(self, other):
return mycmp(self.obj, other.obj) >= 0
__hash__ = None
return K |
Excellent! That's going to be a valuable thing for simplifying the design! |
I'm not managing to come up with a design that I'm satisfied with. I'll do some more work on this, but am thinking that, for now, I won't solve the issue as a mini-framework like I was hoping, but rather collect a few useful tools around the issue. (Perhaps, this is the right design I've been looking for.) Here are my thoughts about these. At the very top level, some contexts will call for a signature binary function (SBF). That is, a Task: extract what ever logic is hardcoded in our code and percolate it to the surface (as a function's argument). By doing this, we're keeping things as they are, while making them open-closed for this issue. A user can always change the behavior by making a different SBF. From there, there's a temptation to go further, because we'll quickly realize that there's a lot of reuse potential when making these SBFs. I propose we don't go further (until the need calls for it), except perhaps documenting the problem patterns we see, and possibly making functions for these (but without trying to fit these in a framework). Here are some problem patterns I see: matching paramsEssentially, adding a layer of information (or structuring, annotating) about the params of each of the signatures (and possibly the
param binary functionsSeveral times in the above, we forwarded the problem down to the "pairs of params" level.
param attribute binary functionsMost of the time, our param binary function will really be just an aggregate (e.g.
The following code (meant to be used as a factory that makes param binary functions through from operator import eq
def param_binary_func(
param1, param2, *, name=eq, kind=eq, default=eq, annotation=eq, aggreg=all
):
return aggreg((
name(param1.name, param2.name),
kind(param1.kind, param2.kind),
default(param1.default, param2.default),
annotation(param1.annotation, param2.annotation),
)) Note that the same |
Though "binary function" is more precisely indicates the generality we need here, I'm wondering if we should sinfully just call it "comparator" since:
Perhaps we can call it "collator" and "comparison->collation"? I really insist on finding a non-composite word because things get out of hand when adding other words to it. Any opinions? |
Regarding using the parameter comparators to make signature comparators, one way is to do: graph
a(sig1) --> matcher
b(sig2) --> matcher
matcher --> matches(matches)
matcher --> remainder(remainder)
matches -- for each match --> param_comparator
param_comparator -- collect --> comparison(comparison)
comparison --> aggreg
remainder --> aggreg
aggreg --> output(final output or error)
Or something like that. But, it could also be: graph
sig1 --> cartesian_product
sig2 --> cartesian_product
cartesian_product -- for each pair --> param_comparator
param_comparator --> comparisons_matrix(comparisons_matrix)
comparisons_matrix --> reduction
reduction --> result(result)
The latter is cleaner, but quite wasteful, and maybe harder to express some simple cases (our most common cases). |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
There's a recurring need for more control over how we compare and/or merge signatures.
These often involve some logic on parameter objects (with their name, kind, and optionally default and annotation), order, etc.
We'd like to have a clean framework that enables the user to easily and correctly define these functions, reusing much of the common logic.
Examples:
sig1 == sig2
: equality (more precisely "equivalence relation") of two signaturessig1 <= sig2
: comparison (more precisely "total order relation" asissubset
,issubclass
etc.)sig1 + sig2
)sig1
to an input value of function with signaturesig2
Don't miss this comment that proposes a design.
Notes
Return type of a comparison
Must cases might just need a Boolean, like with
x == y
orx <= y
(orissubset
,issubclass
, etc.).But in some cases, such as with self-organizing code (or less crazily: validation scores), we'd like to have a numerical (or more generally, vectorial) score, or more generally, some "comparable" type that can be used to do things like "best match".
Use a key function or a binary comparison function as the specifier?
Might want to express comparisons based on key functions like builtin
sorted
,max
etc. do. What are the pros/cons? It seems like a cleaner design enabling easier UX (and obviously, a big pro is the interface-consistency with familiar builtins). But, for example, is it complete? Are there things we cannot do with key functions that we can with the usual binary operator way (specifying comparison function via a two-element boolean function such ascompare(x, y)
).For example, how would comparison scores be derived from key functions? Even if it's possible to "fit" any comparison function with vectorial key functions, would it be the most intuitive way of expressing it?
References
signatures.py
codeRelated issues
code_to_dag
. But that's only forcode_to_dag
. Still, the long issue has some reflections about signature comparison.Further notes
The definition of signature equality
Sig
doesn't redefine it -- the builtin is used, which boils down to using the following (acts basically as a "key function"):The text was updated successfully, but these errors were encountered: