Programming Languages

In order to create programs, code must be written to communicate with the computer, giving it instructions to execute. These instructions end up becoming binary values, ones and zeroes, and executed from there. However, as programmers, we don't write in binary. Instead, programming languages are used to allow programmers to write code in a way that makes logical sense, which is then converted into machine instructions. These programming languages often use keywords in place of certain machine instructions that eventually get read by the computer. There are many different programming languages with different benefits, tradeoffs, purposes, and objectives. Some will execute code faster, while some will allow for safer and easier code to be written. There are many reasons for choosing a programming language when writing code and programming, with some of those reasons coming down to implementation, paradigms, and type systems.

Implementation

In order for programming languages to go from code to executable instruction, it must be either compiled or interpreted. These two implementations impact the amount of time it takes to execute code, number of steps needed before executing code, and the preparation that occurs each time the code is executed.

Compiled

Compiled programming languages take the code and compile it directly into machine code. This requires an additional build step to take the code and convert it into machine code, however because this code doesn't get interpreted line by line through an intermediary, compiled programming languages historically execute faster than interpreted programming languages.
Examples of Compiled Programming Languages are: C, C#, Rust, Go, and Java.

Interpreted

Interpreted programming languages take the code, read it in some intermediary point, and execute that code there line by line. Due to this intermediary point, interpreted programming languages historically were slower than compiled programming languages. However, this speed gap is shrinking with further advancements in the design and implementation of programming languages.
Examples of Interpreted Programming Languages are: Python, JavaScript, Ruby, Lua, and Perl.

Paradigms

Implementations of programming languages aren't the only aspects that differentiate programming languages. Another difference comes in the form of Programming Language Paradigms. These paradigms are different styles of programming that affect how code is written and executed in those programming languages. These paradigms also affect the reasons behind choosing one language over another, as one paradigm may be better suited towards solving a specific problem over another paradigm. In this section, three different paradigms with examples will be given. These three paradigms are procedural, object oriented, and functional.

Procedural

Procedural programming is the style of programming that utilizes procedures and emphasizes a top-down approach to programming. This top-down approach means that code will execute from the top to the bottom, relying on procedures being defined and called at various points in the code. These procedures, or functions, consist of blocks of code that can be called under a defined name. This concept allows for developing reusable code. Additional to procedures, procedural programming also allows for scoped state. This state refers to data that exists in certain spaces within the code. There are two main forms of state in Procedural programming. The first is Global State. This refers to state that remains accessible throughout the program. This can be changed within functions, but exist everywhere below where it is defined. The second is Local State. This refers to state that exists within a function. Local state exists within that function or if it is passed into another function. However, unlike Global State, it isn't defined outside of the function. The ability to work with a Global State and passing changeable data between functions are more unique to Procedural programming than the other paradigms. In terms of its use, Procedural programming is common for general-purpose programming as it allows for the development and reuse of functions, along with the ability to have a changeable global state.
An example of Procedural programming is C.

Object Oriented

Object Oriented programming is the style of programming that utilizes classes and objects. In this paradigm, everything is an object. An object is a data type that contains other data types. It can be created using classes. A class serves as a blueprint for the type, containing internal types, variables called members, and functions called methods. Classes allow for the encapsulation of data, hiding or showing data depending on the needs of the class. In Object Oriented Programming, there are also ways of inheriting other classes, extending the functionality of a class to cater to different purposes.
Examples of Object Oriented programming are Java, C#, and Kotlin.

Functional

Functional programming is the style of programming where everything must be a function. In this case, functions are also treated as first-class citizens. This means that they can be named, used as arguments in other functions, and returned from other functions. In this case, functions act as a callable data type. Additionally, functional programming allows for chaining functions together. This is important as it can be used to develop more modular code. Additionally, functional programming uses pure functions where functions have no side effects – where data outside of the function changes – and given any input, the output will change in a constant manner.
Examples of Functional Programming languages are Haskell, Clojure, and Erlang.

Multi-Paradigm

There is a fourth paradigm is called Multi-Paradigm. This is the case where you have multiple paradigms available for the programming language and can be used together. For example, Python is a multi-paradigm language, where all three listed paradigms can be used together. Global variables can be used, thus making use of the global state attribute from the Procedural paradigm. Classes can be written to develop unique object types to cater to the needs of a program, thus utilizing the Object oriented paradigm. Functions can also be passed as variables and called purely without side effects in a functional paradigm manner. While this is a simple example of all three being used, there are other manners in which all three are used. This book will explore all three in some detail, providing examples of using each paradigm in Python.
Other examples of Multi-Paradigm programming languages are TypeScript, C++, Rust, and Go.

Type Systems

Outside of programming language paradigms, there is another component that impacts the choice of a programming language over another. This is in the form of the type system that the language uses. There are tradeoffs to these type systems that can impact the time it takes to compile or interpret code, run/execute the code, or write code in the language. Two contrasting systems are explained below briefly to provide an introductory understanding of the type systems. The first comes in the form of type checking and when it occurs. This is the difference between Static and Dynamic type checking. The second is a Strongly typed language and a Weakly typed language.

Static v Dynamic

The first contrasting type system comes in the form of the different means of type checking. A statically typed language/statically checked language will check the type of a variable when it's being compiled. This can impact the amount of time it takes before a program is run as it has to check the types of variables before executing it. This can ultimately save time in the future as the compiler does the checks while compiling the code. In this case, the check doesn't happen each time the code is run, but only when being compiled. This does often come with the tradeoff of the programmer having to define the types and ensure that types of variables are set before compiling. However, again, this often leads to a faster execution time, but a slower time while compiling the code. In contrast, a dynamically typed language/dynamically checked language will check the type of a variable during run time, or when the program is being executed. Due to the check occurring at run time, this often leads to a slower execution time for the code as it has to check for the type each time the code is run. This often is paired with a weaker typed language, allowing the programming to have to write less code before executing.

Examples of Statically Typed Languages are: C, C++, Java, Rust, and Go.
Examples of Dynamically Typed Languages are: Python, Ruby, JavaScript, and PHP.

Strong v Weak

The second distinction is between Strongly typed languages and Weakly typed languages. This is called type safety. A strongly typed language has stronger rules for variable typings and ensure that a variable remains a certain type. This does not strictly mean that variables have to have a type defined necessarily, as variable types can be inferred by the compiler or interpreter, as seen in Python. With a strongly typed language, there are restrictions on how data types can be mixed. Additionally, implicit casts between data types do not occur in a strongly typed language. For example, adding an integer and a string would not execute and throw an error of sorts. However, in a weakly typed language, there are no restrictions on how data types can be mixed. In contrast to the strongly typed language with implicit casts not occurring, implicit casts can occur in a weakly typed language. This means the interpreter or compiler can convert an int into a string when a string could make more sense in that situation. An example can be seen in JavaScript where adding the integer of 1 and the string of "1" produces the string of "11", or subtracting the string of "2" from the integer of 1 produces the integer of -1. A misconception is that the type must be defined for the language to be considered strongly or weakly typed. Type inferences can occur in both a strongly typed language and a weakly typed language, but implicit casting between types can only happen in a weakly typed language.

Examples of Strongly Typed Languages are: Java, Rust, and Python.
Examples of Weakly Typed Languages are: C, JavaScript, and PHP.