A programming language is a formal system of notation and rules used to express computations, algorithms, data transformations, interactions with hardware or other software, and constraints on how a computer program should behave. A language normally defines syntax, which determines how valid programs are written, and semantics, which determine what those programs mean or how they execute.
Many languages also specify a type system, standard library interfaces, execution model, module system, error-handling mechanisms, and rules governing names, scope, storage, concurrency, or evaluation. Programming languages exist at different abstraction levels. Machine language represents instructions in forms directly executed by a particular processor architecture, while assembly language provides symbolic representations of such instructions. Higher-level languages such as C, Java, Python, Rust and Haskell allow programmers to express operations using abstractions that are substantially removed from individual processor instructions. The central purpose of a programming language is therefore not merely to provide a convenient way of writing text that a computer can execute, but to establish a precise vocabulary and computational model through which humans can specify behaviour.
Programming languages differ substantially in how they represent computation and how much responsibility they assign to the programmer. Some are statically typed, meaning that type relationships are checked substantially before execution, while dynamically typed languages perform important type checks during execution. Some use nominal type systems, structural type systems, gradual typing, dependent types, algebraic data types, prototype-based objects, class-based objects, or combinations of these mechanisms.
Memory management also varies: C commonly exposes explicit allocation and deallocation, languages such as Java and Go use garbage collection, and Rust uses ownership and borrowing rules to enforce many memory-safety properties at compile time without requiring a tracing garbage collector. Evaluation strategies can include eager evaluation, lazy evaluation, strict evaluation, call-by-value, call-by-reference and other models. These choices affect performance, safety, expressiveness, interoperability and the kinds of errors that can be detected before a program runs.
The history of programming languages reflects the development of computing from direct machine control towards increasingly powerful abstractions. Early programmers frequently worked with machine instructions or assembly language, but higher-level languages emerged to reduce the amount of hardware-specific detail programmers needed to manage. FORTRAN, first released in the 1950s, was influential in scientific and numerical computing, while Lisp introduced important ideas concerning symbolic processing and functional programming.
COBOL became important for business data processing, ALGOL strongly influenced later language design, and C became particularly influential in systems programming. Later languages such as Smalltalk, C++, Objective-C, Java, Python, JavaScript, C#, Haskell, Go, Rust, Swift and many others addressed different combinations of portability, safety, productivity, object orientation, functional abstraction, concurrency and systems control. Languages are continually revised through standards, language specifications, implementation practice and community conventions, so the meaning of a language may encompass both a formal specification and an evolving ecosystem.
A programming language is implemented through mechanisms that translate or execute its source programs. A compiler generally transforms source code into another representation, such as machine code, bytecode, intermediate representation or another programming language. An interpreter executes a program through an execution mechanism rather than necessarily producing a permanent native executable beforehand. Many modern implementations combine these approaches through parsing, semantic analysis, intermediate representations, optimisation, interpretation, just-in-time compilation and runtime services.
Assemblers translate assembly language into machine or object code, while linkers combine object files and libraries and resolve references between separately compiled components. Runtime systems can provide services such as memory allocation, garbage collection, exception handling, thread management, dynamic dispatch and interaction with operating-system facilities. Consequently, understanding a programming language in practice requires distinguishing the language itself from the compiler, interpreter, virtual machine, runtime library and development tools that implement it.
Programming languages are used in different environments because their abstractions and implementation strategies suit different requirements. Systems languages can provide close control over memory layout, processor operations and operating-system interfaces. General-purpose languages can support web applications, desktop software, automation, scientific computing, data processing and business systems. Domain-specific languages are designed around narrower problem areas, such as SQL for relational database operations, regular-expression languages for pattern matching, shader languages for graphics processing, and hardware-description languages for specifying digital circuits.
Some languages are embedded within another language or application and can be used as configuration or scripting mechanisms. A language can also have multiple implementation targets, such as native processors, browser virtual machines, managed runtimes, embedded devices or WebAssembly environments. Portability therefore depends not only on syntax but also on available implementations, standard libraries, operating-system facilities, processor architectures and external dependencies.
Programming-language design involves trade-offs rather than a universally optimal collection of features. Greater abstraction can improve programmer productivity and reduce certain classes of errors, but may introduce runtime costs or obscure lower-level behaviour. Explicit control can improve predictability and performance while increasing complexity and the opportunity for programming errors. Strong static checking can detect many inconsistencies before execution but may require programmers to express additional type information or satisfy stricter constraints.
Dynamic techniques can make rapid experimentation convenient but can defer certain errors until execution. Automatic memory management can remove many manual memory-management responsibilities while introducing collection overhead or less predictable resource reclamation. Language designers therefore consider expressiveness, readability, safety, performance, portability, implementation complexity, interoperability, tooling and compatibility. The design of one feature can also interact with apparently unrelated features, meaning that language semantics often result from complex combinations of design decisions rather than isolated mechanisms.
Programming languages are often confused with programming paradigms, libraries and frameworks, but these are different concepts. A language may support several paradigms, and a paradigm describes a style or conceptual model for organising computation rather than simply a particular syntax. A library provides reusable functionality that programs can call, whereas the language specifies fundamental constructs and rules governing programs.
A framework typically imposes architectural conventions and controls parts of an application's execution, while the language itself does not necessarily dictate a complete application architecture. Likewise, a compiler is an implementation tool rather than the language, and an interpreter is an execution mechanism rather than a definition of the language. The same language can have multiple independent implementations, and different implementations can target different hardware or execution environments while remaining substantially compatible with the same language specification.
Important properties of programming languages include abstraction, composability, determinism, portability, safety, expressiveness and interoperability. Language specifications can define observable behaviour while leaving implementation strategies unspecified, allowing compilers to optimise programs as long as required semantics are preserved. Undefined behaviour in some languages permits implementations substantial freedom when programs violate particular requirements, whereas languages with stronger semantic guarantees may define more cases explicitly. Language ecosystems also depend on package managers, build systems, debuggers, formatters, static analysers, testing tools and documentation.
A language can therefore be technically elegant yet have limited practical adoption if its tooling or ecosystem is inadequate, while a technically older language can remain important because of existing software, libraries, standards and institutional investment. Accurate understanding requires separating language features from implementation details, historical popularity from technical properties, and programming-language concepts from the tools used to develop and execute programs.