-2

I started adding informations about the behavior of functions to their name recently.

Two big examples:

  • pure - this function has no side effects (besides the returned value), which I would use in Java like static int addingNumbersPure(int a, int b) (bad example as no one would expect side effects in such a method, but you get my drift).
  • linearTime - the big-o runtime complexity of the code, for example Object searchPureLogTime(Object[] sortedArray).

This does give me (and others calling my code) deeper insight into what those methods do - useful in larger project - but on the other hand, I don't feel comfortable using it in interface definitions, as they could be extended with other functionality, breaking this contract. So obviously I only use it for methods that are not designed to be extended (however that is implemented, via language keywords or the architecture of the project itself).

An alternative I used before was Javadocs (or whatever doc system was used in a project) to add those informations, but I don't know of any IDE inlining those docs into the current editor - therefore reading the code as a block does not give the reader those informations.

In the end, I'm conflicted. Is my way of adding those information better or worse than just not giving that information? Is there a better, tool-agnostic way of doing it? What's the industry standard?

PS: While this question is language agnostic, it's obviously irrelevant in languages like Haskell where for example pure is no new information.

  • 2
    Interesting idea. My knee-jerk reaction is that it is not a good thing. It is similar to adding type to variable name. Both examples you present have their problems. And I can't think of any other use case. – Euphoric Sep 30 '17 at 9:24
7

I don't like it. Both your examples have problems.

When it comes to the Pure one, the annotation should be readable primarily by static analysis tools. Those can check if it is truly pure and make sure it is called properly. And I don't believe many tools use method name for that. For example, C# has PureAttribute for exactly this reason.

In general case, primary purpose of method name is to tell me, the client, which should I use. If you have just a single method with single time complexity, what choice do I have? It would only make sense if you had two methods, each doing the same thing, but with different complexity. But then, why have method with worse complexity in the first place? As such, it feels like useless noise.

Another problem I worry about is that it increases risk of function name lying. This happens, when you change inner working of the function without changing the name. Even worse, when the function is published one, where you cannot change the name.

Another question is when should those "annotations" be used? Obviously, it should not be fore every method, as that would create tons of overhead. Then it becomes a battle whenever some specific method should or should not have the annotation.

tl;dr; Only really add such annotation if there are multiple similar methods and such annotation would provide me, the client, critical information on which method is right for my use. And if such information cannot be provided through language or framework feature. Otherwise, documentation is plenty in cases where I might be interested in such information.

3

To extend on @Euphoric's answer:

Things like big-O runtime should not become part of the contract, as then replacing the implementation with a faster algorithm will break the existing contract or create a new function with a new contract. And I guess you don't want to name your method searchPureAtMostNSquareTime(Object[] sortedArray).

And if your method takes more than one parameter (e.g. two lists), how do you express the information if it's O(log(list1.size())) or O(log(list2.size()))? You'll end up with completely unreadable names.

Let's come to readability of code. I'd definitely prefer to read search(array) instead of searchPureLogTime(array). With the latter I have to mentally parse the name and strip the suffixes until I find that it's just a "search" call.

I surely see the benefit of the informations that you want to provide, but you should find a different way of doing it, maybe create your own annotations.

  • Good points, especially regarding readability. But I do have to point out that Big-O complexity is an upper bound, so the implementation would be free to use a better implementation – exact bounds/Theta or lower bounds/Omega on complexity are rarely used. Notably, C++ specifies the complexities of many standard library functions, though these aren't encoded in the name. – amon Sep 30 '17 at 20:49
2

I agree with the other answers, adding the complexity to the method signature breaks the encapsulation, as you cannot change the implementation without influencing the signature of the method (in case that the new algorithm improves the performance for example).

However, I think the pure case is trickier. I think that comes down to the expectations that your library is providing. If you are designing one, and every function is pure that should be reflected in the design docs. To take an example from another language in ramda all the functions return copies and are curried, so your expecation for each call should be the same, without having to include this information in the method name.

Another example that you could use for inspiration would be ruby. There, in certain situations, there will be two versions of a method, one with ! and one without. So for example, sort would return a copy of the array, and sort! would modify it in place. Note that the convention is not used consistently, as there are destructive methods without the bang, and methods with a bang that don't have to deal with "pureness" at all (such as save and save! for ActiveRecord)

In the case of Java, I guess the most accepted practice would be to use an annotation like Pure.

  • 1
    The convention you describe is from Scheme, not Ruby. In the Ruby community, the bang means something different: it is applied to the "more surprising" of a pair of methods. So, a) there is only a bang if there are two methods, and b) the bang does not mean "destructive", it means "more surprising than the other method of the pair". For example: Kernel#exit! and Process::exit! are not destructive, yet they have a bang, and Array#replace, Array#clear, Array#pop, Array#push, Array#shift, Array#unshift, Array#[]=, Array#<< are all destructive, yet have no bang. – Jörg W Mittag Sep 30 '17 at 16:24
  • you are right, my answer was a bit misleading. I updated it to have a more accurate description. – Mario F Sep 30 '17 at 18:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.