Some languages, such as C++ and Java, have grammar restrictions where code that is not a declaration or import statement cannot be outside of a function. However, variables, functions, and classes can be freely created at the 'top level'. Other statements are allowed inside functions, where the main function effectively acts as the top level.

This is in contrast to a language like Python which freely allows if statements, loops, and all other constructs to be outside of any functions in the top-level code environment.

What are the pros and/or cons of either approach? In which types of languages might it be useful to enforce declaration only top levels and when might it be alright to allow all kinds of statements at the top level?

  • 1
    $\begingroup$ The third alternative is languages like Swift and C#, where you can have top-level code in up to one file, or you can have a main function. $\endgroup$
    – Bbrk24
    Commented May 24, 2023 at 12:48
  • $\begingroup$ Related question on CS SE: cs.stackexchange.com/q/160004/111482 $\endgroup$
    – kaya3
    Commented May 24, 2023 at 14:06
  • $\begingroup$ At a high level: whenever a thing can’t be nested in something else, you may need to “normalise” the program that you’d rather write, and like expanding a Boolean formula to DNF or CNF, this may scale exponentially. To avoid that, programmers tend to add more names and other abstractions. But the root cause is that the thing isn’t first-class. It’s not necessarily a problem—flatter structures are often easier to understand—but it is a tradeoff. $\endgroup$
    – Jon Purdy
    Commented May 24, 2023 at 21:26

3 Answers 3


Whether to allow top-level statements is mostly a matter of technical considerations.

In compiled languages, statically typed languages, it is necessary to create a complete list of top-level functions to be able to properly compile them. For compiled programs these functions may be exposed individually and called by external programs that may link to your bundle. Fundamentally your program will compile into a set of functions with maybe some related data, perhaps with also a entry point which is just another function and can also be called by external libraries.

On the other hand, in more dynamic interpreted languages functions may be created, destroyed, and modified on the fly. A function declaration is just another type of statement, and it won't exist until then. There is no real difference between the inside and outside the function, all code will be run one by one top to bottom. If functions are just expressions it makes sense to allow other expressions too where functions are allowed.

If you fall into neither of these categories, the choice is up to you. Allowing only functions at the top level can make type checking easier, but also adds more boilerplate.


Boilerplate for small projects

Java has an infamously verbose “hello world” due to the amount of boilerplate required to run anything. Python and JS, on the other hand, both have one-liners.

Imports with side effects

When you require a file in JS, all top-level code in that file is run, which can have side effects. Sometimes this is useful (e.g. a master test script can just require each individual test), but usually this is unwanted. Some languages, like Swift and C#, get around this by only allowing one file per project with top-level code.

  • $\begingroup$ Hello, world, is getting shorter in the next version of Java (not very smart, I'd say, if you read the relevant JEP) $\endgroup$
    – Seggan
    Commented May 24, 2023 at 14:23

TLDR: Such constructs aren't supportable in all execution environments.

Many high-level language compilers don't generate executable programs directly, but instead produce object files which will then be fed to a linker in order to generate an executable. Some linkers may provide a mechanism by which multiple object files can contain code which should be run at program startup, without any of the object files knowing about each others' initialization code, but not all linkers support such things. A language which allows source files to include code which isn't in functions, and which should be run at program startup, would either be unusable with linkers that can't support such constructs, or would cause the range of constructs available to programmers to vary depending upon the linker used. Such compromises may be acceptable for some language designers, but many prefer to limit languages features to those that linkers can universally support.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .