A high-level language provides features that standardize common tasks, permit rich debugging, and maintain architectural agnosticism. On the other hand, a low-level language requires the coder to work at a lower-level of abstraction which is generally more challenging, but does allow for
optimizations that are not possible with a high-level language. This
abstraction penalty for using a high-level language instead of a low-level language is real, but in practice, low-level optimizations rarely improve performance at the
user experience level. None the less, code that needs to run quickly and efficiently may require the use of a lower-level language, even if a higher-level language would make the coding easier to write and maintain. In many cases, critical portions of a program mostly in a high-level language are coded in assembly in order to meet tight timing or memory constraints. A well-designed compiler for a high-level language can produce code comparable in efficiency to what could be coded by hand in assembly, and the higher-level abstractions sometimes allow for optimizations that beat the performance of hand-coded assembly. Since a high-level language is designed independent of a specific computing
system architecture, a program written in such a language can run on any computing context with a compatible compiler or interpreter. Unlike a low-level language that is inherently tied to processor hardware, a high-level language can be improved, and new high-level languages can evolve from others with the goal of aggregating the most popular constructs with improved features. For example,
Scala maintains backward compatibility with
Java. Code written in Java continues to be usable even if a developer switches to Scala. This makes the transition easier and extends the lifespan of a
codebase. In contrast, low-level programs rarely survive beyond the
system architecture which they were written for. == Relative meaning ==