Log, Right?

Posted on Jan 16, 2020

Most Machine Learning projects have seen me write a function to log data to a log-file as well as apply a logarithm to a vector. As I’m not aware of a conventionally used synonym for the former use of ’log’ and the latter is usually referred to as ’log’ in mathematics (thereby rejecting the argument I would usually bring up in naming issues: avoid abbreviations and acronyms) a naming problem naturally arises. What’s log?

So let’s take a step back and formalize our desires from naming.

Goals

  1. Given the semantics of a function, the origin of its name should be comprehensible.
  2. Given only a function name, the rough semantics should be clear.
  3. Given the semantics of a function, the function should be named after the first name the user would associate with the semantics. [0]

The first goal should be self-evident and be realized by any thinkable suggestion. While achieving the latter goal might be the pinnacle of interface design, I’m afraid it is unreachable in this context. Many people will just want to give the operation they have in mind the name log - a naming conflict. Hence we will evaluate options by whether they manage to realize the second goal.

Options

For the sake of concreteness, let’s define both semantics:

import numpy as np

def fn1(data):
	log_writer = LogWriter('log.csv')
	log_writer.write(data)

def fn2(x):
	return np.log(x)

Option 1: log, logarithmize/convert_to_log,

  • Cumbersome
  • Doesn’t solve goal 2
  • Possible hit, possibly confusion wrt goal 3

Option 2: write_to_log, log

Trade-off equal (resp. symmetric) to option 1.

Option 2: write_to_log, convert_to_log

  • Double cumbersome
  • Solves goal 2
  • No hit and no confusion wrt goal 3

After many late-night executive decisions to go with either option 1 or 2 for no reason but tiredness I will from now on side with option 3.

Addendum 1: Type signatures might help by hinting at the problem, but don’t solve the problem.

Type hints, both with respect to function parameters as well as return values, in combination with some IDE or editor features parsing function signatures, might provide a lot of ease and value to the user. More precisely, they can protect against overconfidence purely derived from the name of a function. E.g. a user looking for a mathematical log, finding out about the existence of a function called log might blindly trust that it applies the mathematical log. With the following definition:

import numpy as np
from nptyping import Array

def log(x: Array[float]) -> None:
	filename = 'log.csv'
    	with open(filename, mode='a+') as log_file:
        	log_writer = csv.writer(log_file, delimiter='|', quotechar='',
                                	quoting=csv.QUOTE_NONE, escapechar='\\')
        	log_writer.writerow(data)

the existence of a problem would usually become apparent. Note that only usually, as a user would, as I see it, usually expect a copy of the array to be returned. In case the user assumes an in-place/by reference update, it doesn’t help at all. Moreover, it is not constructive in avoiding the problem.

Addendum 2: Offloading responsibility from naming to commenting.

While this could under some circumstances be a useful addition, I have two problems with this.

  1. I try to respect the general approach of using comments to describe how I’m doing things. I intend to clarify the what via naming.
  2. This alone only fulfills goal 2 under the assumption that users always read function signatures before usage.

Addendum 3: Why bother?

Naturally, I’d ask myself the question: Why bother with all of this naming? It is not perfectly clear that it is worth the effort. What is clear is that there is upside to it.

  1. In general, knowing that a cost will have to be paid, it is a good idea to pay it upfront to a time of my choice than having to do it on-demand. Expense increases with the lack of flexibility.
  2. It seems to me that such structural, engineering practices vehemently rely on consistency. Both users and I only truly start profiting from such upsides if we all have the feeling that they hold at least ‘almost all of the time’[1].
  3. Moreover, I like to think of this thought process as practice. I will face very similar decisions time and time again and thinking about them earlier will make it easier to make good calls later.

[0] Note that there is another possible distinction to be made here: It can easily be argued that not having a ‘hit’ is way superior to having a ‘hit’ with semantics differing from expectation.

[1] Think of a sigmoid function. Perceived value = sigmoid(likelihood of success of intuition) with a vertical inflection point[2] somewhere between 0.9 and 1.

[2] I think inflection points are ‘horizontal’ by definition, please forgive the abuse of terminology. :)