I think exposing inverse
in addition to inv
would have two benefits:
- It would allow code to be written in a way that is more intuitive and self-explanatory.
- It would help with discoverability - only a little, but enough to be worthwhile.
1. Intuition and Self-Explanation
I think it's important to remember that there is a big difference between reading .inv
when:
- You have the word "inverse" or the concept of inversion already in mind.
- You don't have the word "inverse" or the concept of inversion in mind.
- You couldn't even say when you last used or read the word "inverse".
- You don't know the word "inverse".
.inv
is really great in the first case, and is decently suggestive in the second case, and it's definitely easier to type, so I like it and think it's good to have it. But I think .inv
can be needlessly hard in the third and fourth case.
Because inverse
is not a "casual" word - it's easy to forget when we've internalized it and work with inverses of things regularly, but most people first explicitly learn it in technical contexts, like in math or formal logic or computer science.
For many practicing software developers the word itself doesn't come up that often, because when the concept does come up, it can be easier to find more common words to express it.
Relatedly, more and more software developers, both professionally and in open source communities, have English as their second language, or have picked up software as a practical tool without the more formal background.
I think when people are trying to understand some code, whether because they're new to it or haven't worked with it in a long time, having .inverse
reduces the amount of mental work they have to use just to idea-fit what the intent is, relative to .inv
, especially if they're not yet familiar with bidict
as a package.
TL;DR: I love .inv
for interactive use and quick work, but I'd like to have the option to write .inverse
when it helps people read and understand my code in the long term.
2. Discoverability
I recently tried using bidict
for the first time after not thinking about it for maybe four or six months, and I couldn't for the life of me remember off the top of my head how to get the inverse view.
Naturally, because Python is so helpfully discoverable, I did dir(my_bydict_instance)
and scanned the output (line-wrapped like it was on my 80-characters-wide terminal):
>>> dir(bd)
['_MutableMapping__marker', '_ON_DUP_OVERWRITE', '__abstractmethods__', '__class
__', '__contains__', '__copy__', '__delattr__', '__delitem__', '__dir__', '__doc
__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__get
state__', '__gt__', '__hash__', '__init__', '__inverted__', '__iter__', '__le__'
, '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduc
e_ex__', '__repr__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__',
'__slots__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_
negative_cache', '_abc_negative_cache_version', '_abc_registry', '_dedup_item',
'_fwdm', '_fwdm_cls', '_get_on_dup', '_hash', '_init_inv', '_inv', '_inv_cls', '
_invm', '_invm_cls', '_invweak', '_isdupitem', '_isinv', '_pop', '_put', '_repr_
delegate', '_undo_write', '_update', '_update_with_rollback', '_write_item', 'cl
ear', 'copy', 'forceput', 'forceupdate', 'get', 'inv', 'items', 'keys', 'on_dup_
key', 'on_dup_kv', 'on_dup_val', 'pop', 'popitem', 'put', 'putall', 'setdefault'
, 'update', 'values']
For me, inv
just blended in with the other smaller default attributes, and more importantly, it didn't visually pattern-match for the kind of word I most readily thought of while trying to articulate in my head what I wanted.
Consider the appearance if we add an inverse
alias in there:
>>> dir(bd)
['_MutableMapping__marker', '_ON_DUP_OVERWRITE', '__abstractmethods__', '__class
__', '__contains__', '__copy__', '__delattr__', '__delitem__', '__dir__', '__doc
__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__get
state__', '__gt__', '__hash__', '__init__', '__inverted__', '__iter__', '__le__'
, '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduc
e_ex__', '__repr__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__',
'__slots__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_
negative_cache', '_abc_negative_cache_version', '_abc_registry', '_dedup_item',
'_fwdm', '_fwdm_cls', '_get_on_dup', '_hash', '_init_inv', '_inv', '_inv_cls', '
_invm', '_invm_cls', '_invweak', '_isdupitem', '_isinv', '_pop', '_put', '_repr_
delegate', '_undo_write', '_update', '_update_with_rollback', '_write_item', 'cl
ear', 'copy', 'forceput', 'forceupdate', 'get', 'inv', 'inverse', 'items', 'keys
', 'on_dup_key', 'on_dup_kv', 'on_dup_val', 'pop', 'popitem', 'put', 'putall', '
setdefault', 'update', 'values']
The difference is slight and subtle, but I think it really helps, because if you don't know what word to look for, the word "inverse" jumps out more, and is more likely to visually pattern-match for a brain thinking of other related words like "reverse".
To be clear on that last bit, I fully agree that "inverse" is the more technically correct word than "reverse", and with the reasoning expressed in the Terminology addendum of the docs - that's not what I'm talking about. I'm just saying that we know some people think of "reverse" intuitively when thinking about a bijected mapping, and so having something that visually strongly pattern-matches it is a bonus to discoverability.
TL;DR: I think inverse
is more likely to more quickly jump out to a person unless they already know to look for inv
, especially if they have the word or similar words in mind already.
Aside on using dir
as example to discoverability:
Someone will probably want to reply that I could've just looked up the documentation for bidict
on the web instead, or that this would not be an issue on a modern IDE, or that doing help(my_bidict_instance)
would've made it easier, so I'm just going to preemptively address those now:
-
Web searches relative to dir
is like the human equivalent of a CPU getting data from disk instead of from L1 cache: it's must more out-of-band and requires orders of magnitude more mental, physical, and temporal overhead, as well as additional external conditions. It's easy to tune it out if you're already used to doing that, because human brains are good at that, but it's really noticeable when you're used to not having to do it.
-
IDEs and IPython and so on reduce the information-to-noise ratio vs. dir
, and can sometimes intelligently display type hints/inferences - still, as I'm sitting here looking at the auto-complete for my_bidict_instance.
in a couple of them, in moments when I manage to clear my mind of expectations, inv
doesn't really stand out prominently either. It's a lot easier to find, but the discoverability inefficiency isn't so much removed, as "dimmed". (Plus we don't always have access to such conveniences, depending on the systems or situations we're dealing with.)
-
help
provides a completely different kind of discoverability than dir
provides: help
gives you far more information to wade through, and all that extra information relative to dir
is about how the interfaces work, and is useless for the goal of remembering what a specific interface you already have in mind is called. It reduces information-to-noise ratio to use dir
instead of help
in such cases.
In short, my habit of calling dir
first developed because it gets me the right information faster most of the time, despite the unpleasantness and overhead of visually parsing the attribute names from the forest of single-quotes.
But even if dir
still doesn't feel like a good example, the overall discoverability point still applies, and even if the discoverability gain doesn't seem worth it, I think the intuition-and-reading-guiding obviousness of the full word is the main argument.