Skip to content
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

Role-based names vs. Information Repetition #47

Open
xtofl opened this issue Feb 21, 2021 · 1 comment
Open

Role-based names vs. Information Repetition #47

xtofl opened this issue Feb 21, 2021 · 1 comment

Comments

@xtofl
Copy link

xtofl commented Feb 21, 2021

The words 'handler', 'callback', ... are often used to add a notion of where a variable/function is going to be used. However, this is very much as useless as naming a variable after its type.

As an example, consider some library to represent countries on a geographical map or so...

# library code
class Country(Widget):
  ...
  def on_click(self, click_handler):
    self.click_handlers.append(click_handler)

And we want to use this library to e.g. draw dots on the respective countries.

# main code

# example: BAD
def my_click_handler(source, click_event):
  paint_circle(source.country_colors, click_event.coordinates)

for _, country in world_map.items():
  country.on_click(my_click_handler)  # BAD: I already expect that `on_click` accepts a click handler 

In this code, the my_click_handler name is meaningless. When reading the main file, you have to read up to the body of the click handler function to know what should happen when a country is clicked.

Imagine a more extreme example:

def divide(nominator, denominator):
  return nominator / denominator

class Vehicle:
  def speed(self):
     nominator = sum(self.distances)
     denominator = time.now() - self.journey_start.time
     return divide(nominator, denominator)   # BAD: this is a repetition of the function parameter names

In both cases, the information in the variable names (my_click_handler, nominator, denominator) are mere repetitions of the parameter names of the called function. This information is already present in the function signature itself, and must not be repeated.

Rather, when the names are chosen to reflect the role the variable/function plays in the program, it becomes much more meaningful:start_time

# example: GOOD
# - the function name reflects what I want to _achieve_ with it
# - the function parameter tells me that I expect a _country_
def mark(country, click_event):
  paint_circle(country.colors, click_event.coordinates)

for _, country in world_map.items():
  country.on_click(mark)  # GOOD: name gives new information in terms of 
class Vehicle:
  def speed(self):
     total_distance = sum(self.distances)
     elapsed_time = time.now() - self.journey_start.time
     return divide(total_distance, elapsed_time)   # GOOD: this represents the physical truth of the program

This naming pattern happens very often for the simple reason that it seems that the role of the variable is determined by the called function (a dependency, external to our own context), and needs no thought.

However, this does not add information and does not help the reader of the code to understand it. Rather, choosing the name according to the role within the context of the own problem domain gives the reader insight in its effective meaning.

@xtofl xtofl changed the title Roles vs. Recipients Role-based names vs. Information Repetition Feb 21, 2021
@kettanaito
Copy link
Owner

Hey, @xtofl.

This is a great dive into meaningful callback/handler names. We don't currently recommend how you should name your handlers, and the A/HC/LC pattern is still applicable to functions like mark, which stand for a meaningful action passed to wherever you choose.

There's one nuance about naming handlers after their intention as opposed to after their application:

handleClickOutside // application
refetchUsers // intention

When preferring the intention, there's always a danger of that intention migrating away or growing out of proportion in time. Let's take your country's example:

country.on_click(mark)

Initially, mark circles the clicked country on the map. That sounds great. Now, in time, we've also decided it to display some country info next to the circle. So it also displays the info, that's still under the "mark" intention. Well, then, we also wanted to fetch some weather forecast in that country and render it right next to the country name. Now we also do this side-effect... I hope you're getting the point. We (developers) tend to let our intentions grow and change but it's not a general practice to make sure that function names stay aligned with the intention. Partially, because intentions may become complex, and representing them in a function name gets too verbose:

country.on_click(mark_and_show_weather_also_maybe_track_analytics_if_signed_in)

That's my argument for considering application-based names like handleCountryClick. I agree that it doesn't give you the intention from the function name, but it gives you enough contextual information (hey, this is called when I click on a country) to get started. This way we encapsulate the complexity of our intentions within the function itself, and the handler name will remain true as long as it's passed to country.on_click.

I also think that function names alone are insufficient sometimes to describe the intention. Mainly, due to the complexity of those intentions that I've described above. That's why it's generally dangerous to derive intentions from function names alone. I'd rather trust tests, as those can be as verbose as the intention requires.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants