Be careful with naming
In a previous post I illustrated how my communications skills are below average. One idea (I will prove useful with side projects such as a parsing of apache web log in map reduce fashion) was strongly rejected because I communicated awkwardly. Therefore, now, I am overly cautious with the words I use.
I never was strong with math and science, that's the reason why I chose physics. I never was able to use the wanted terminology but, since I could see symmetries and singularities I always was able to give the solutions without resorting to too much equations. As a result, since I could find solutions, I never cared to explain in plain words how I found them. I do the same when I code : I don't bother explaining, since I see the solution.
I thought that people would understand code without explanations... How wrong. Code does not tell anything.
I thought that people would understand code without explanations... How wrong. Code does not tell anything.
Telling I wanted to add addition to dict was a screw up
Well, python is a duck typing language, I proposed to enhance a «strong» type. A dict is something that quacks like a dict, run like a dict, flies like a dict. Using the wrong words misled me. Something that is a duck-dict is a Mapping. I should have said I wanted to have Mapping to behave correctly with Addition.
Here is the source of Mapping (See here). These are only abstract methods that everything that want to qualify as a dict must have. As a result, it made my code freakishly easier. Mapping is the abstract class for a any dict like classes.
It makes therefore isinstance safe to use !
How to do it ? Maximum overload
There is an obvious way to do it : overloading addition operators : as described here.
Object is_a or has_a. How to do it monkepatching, composition, defining a factory ? I decided it was time to break my biggest taboo : multiple inheritance !
Since Mapping don't have Adders it is not a problem. For the sake of this article, (and since I want to communicate properly now), I discovered this is known as Mixins in python. Since I am a (sniff) former Perl coder I know them as trait, ruby knows them as behaviour.
Since I thought of add/sub/div/mul not as actual mathematical operations but rather as behaviours and that one of my first dev was a light unit test testing behaviour (associativity, distributivity ...). I said funny coincidence, and that's it, I began recoding VectorDict as mixins.
I am not speaking of freezing the meaning of addition, I am speaking of defining more than one set of consistent sets of behaviour for the basic arithmetical operations according to rules that ensures properties, and that makes them safe to use according to our mathematical intuition. (I am pretty found of non euclidian geometry, so I have some fun ideas in mind).
I am not speaking of freezing the meaning of addition, I am speaking of defining more than one set of consistent sets of behaviour for the basic arithmetical operations according to rules that ensures properties, and that makes them safe to use according to our mathematical intuition. (I am pretty found of non euclidian geometry, so I have some fun ideas in mind).
The strength of a word
Words are strong if they are concise enough, but not too broad. My previous experience with VectorDict told me, that naming mixins at the method level would make class declaration unusable. I know 99% will need only Adder, and 1% would maybe need the rest.
What do you need to make a Mapping actually add (use the + sign) ?
Overloading __add__ ?
Not enough : you then don't support a+=b
__add__ and __iadd__ ?
not enough : you need __radd__ : this is how I do it :
class Adder(): """ making Mapping able to add""" def __add__(self, other): """adder""" copy = self.copy() copy += other return copy def __iinc__(self,number): """in place increment""" for k,v in self.iteritems(): self[k] += number return self def __iadd__(self, other): if not isinstance(other,Mapping): self.__iinc__(other) return self for k,v in other.items() : self[k] = v+self[k] if k in self else v return self def __radd__(self, other): copy=self.copy() if not isinstance(other, Mapping): return copy.__iinc__(other) return copy.__iadd__(other)
With this code you have the following result :
from collections import defaultdict class Dad(Adder,Muler):pass e=Dad(int, { "a" : 3,"b" : Dad(int,dict(a = 2 )) }) e*=2 print e+1 # OUT: defaultdict(, {'a': 7, 'b': defaultdict( , {'a': 5})}) f=Dad(int, { "a" : [],"b" : Dad(int,dict(a = [] )) }) f+=[ 1, ] print f # OUT: defaultdict( , {'a': [1], 'b': defaultdict( , {'a': [1]})})
That is the reason why as a tribute to these two words that helped me (trait, and behaviour) my new package implementing the mixins for Addition is now known as archery
Because I thought it would be fun to import trait (arrows) from archery.
So much more naming problems
There is a coupling that cannot be avoided later on traits for Diver on Muler and Adder. It can seem odd, but you MUST code Adder, than Muler, and only then Subber. As a result I had in mind I would have to make consistent sets of traits (since I do not intend to restrict myself to linear algebrae). And I needed a name that made sense to bundle traits of Adder, Subber, Muler, Diver in a way I was safe. That is archery.quiver. Because, I have found nowhere on internet how to name intuitively a macro behaviour/trait/mixin of mixins that must be coupled.
A quiver is not planned to be only a set of traits, it is also planned to be tested for consistency.
Archery, trait, quiver and ... bow
And since I was having fun having traits and quivers I decided it was a time to have the archery.bow. Bow would assemble traits and quivers in usable class. The problem is how to name :
- a defaultdict with addition ?
- a dict with addition, substraction, division, multiplication
Java style and being hated ?
Name should help !
But writing DefaultDictWithAddition (wich is the easier) might become boring (even for me).
Try the pun and being misunderstood?
D(edault)D(ict) that add, sounds to me as dad therefore Daddy.
Who would call its class Daddy ? And how to call Defaultdict with Subber ? Momma ? I realised I would propably face some criticisms for being too openly sexist and giving useless names.
When you have no solutions just be resourceful
I decided that since I had no choices but to try to respect the minimum contract with code I would do two things :
- have less than 10 chars names
- give an hint on the capacity of the class
Bow naming convention
First with trait, and quivers, you may think you don't need bow, but trust me use them (at least as a template for other mappings).
Second, bows will be classified by their power :
- short bow,
- longbows,
- crossbow
Since I know nothing of archery, I will have fun finding exotic names (like Daikyu which is a Japanese long bow) for the other ones.
The naming Nightmare is not over
https://github.com/jul/archery/blob/master/archery/crafter.py have two functions that are clearly misnamed, and I am short on inspiration.Even crafter smells like a wrong idea.
The Bowyer
Bow( int, { "a" : Bow(int, {"a": 1 } ) )
This being annoying to write I have function that takes a Mapping to convert all dict intricated in a dict it works this way :
make_bow = lambda tree: Bow(int, tree) Bowyer(make_bow, { "a" : { "a" : 1 } } )
The fletcher
This one is clearly misnamed : it generates an iterator on all path to keys and values in the following form : from archery.crafter import fletcher
print [ x for x in fletcher({ "l1": 2, "l2": { "c1" : 1 }, "l3": [ 1 , 2 , 3 ] }) ] # OUT: [["l1", 2], ["l2", "c1", 1], ["l3", [1, 2, 3]]]
(I use it to convert dict/JSON to CSV).
You are welcome to help me on this.
PS : this story is a little shortcut and gives me falsely all credits for the ideas of naming which in real life involved bruno rohée & baptiste mispelon. And, I lied a little bit, I did not find everything at once, but telling the truth might have been confusing.
No comments:
Post a Comment