Best practices for top-level declarations


#1

I’m often unsure if write some functions at top-level or in a singleton object.

As example, I’ve noted the function generateSequence in stdlib is at top-level, but probably it could be declared as Sequence.generate. So why have you preferred this?

Essentially they are equivalent, but there are some differences:

  • top-level declarations are directly available from the code completion list, so they should be easier to find
  • top-level names can clash easily with other declarations
  • declarations at top-level are simplier since they don’t require to nest elements inside an object.

So in your opinion what are the best practices to choose top level declarations?


#2

This is not by any means a full answer, but one thing to consider would be whether the function is independent of an object. Utility functions (think java.util.Math) should not belong in classes as there is nothing object oriented about them. Another case would be “factory” methods that have the name fo the type they create (from calling source code they are indistinguishable from a constructor call), but those functions need to be used judiciously.


#3

The recommended practice is to never use object for creating namespaces, and to always use top-level declarations when possible. We haven’t found name conflicts to be an issue, and if you do get a conflict, you can resolve it using an import with alias.


#4

FWIW I love the way it works in Java. All utility methods may have multiple versions, even the methods in Math have a buddy in StrictMath (which nobody uses, but that’s a different story).

I myself use three different versions of my own pow(long, long), namely checked (throwing on overflow), unchecked (ignoring overflow) and saturated. The best naming I could come up with was SaturatedMath.pow (which is nicely short with a static import).

  • Using saturatedPow would only make sense if I needed to use multiple versions in one class, but I never do.
  • Using multiple packages makes no sense as they’d contains just one file each.

#5

The idiomatic Kotlin way to express that would be import kotlin.math.saturated.pow. Since the Kotlin package structure doesn’t have to match directory structure, there is no issue with creating packages containing a single file - you don’t need to put each of these files into a separate directory.