Skip to content
Image with logo, providing a link to the home page
  • United Stated of America flag, representing the option for the English language.
  • Bandeira do Brasil, simbolizando a opção pelo idioma Português do Brasil.

Learn Programming: Libraries

Example of using libraries in four programming languages: Python, Lua, GDScript and JavaScript.

Image credits: Image created by the author using the program Spectacle.

Requirements

In the introduction to development environments, I have mentioned Python, Lua and JavaScript as good choices of programming languages for beginners. Later, I have commented about GDScript as an option for people who want to program digital games or simulations. For the introductory programming activities, you will need, at least, a development environment configured for one of the previous languages.

If you wish to try programming without configuring an environment, you can use of the online editors that I have created:

However, they do not provide all features offered by interpreters for the languages. Thus, sooner or later, you will need to set up a development environment. If you need to configure one, you can refer to the following resources.

Thus, if you have an Integrated Development Environment (IDE), or a combination of text editor and an interpreter, you are ready to start. The following example assumes that you know how to run code in your chosen language, as presented in the configuration pages.

If you want to use another language, the introduction provides links for configure development environments for the C, C++, Java, LISP, Prolog, and SQL (with SQLite) languages. In many languages, it suffices to follow the models from the experimentation section to modify syntax, commands and functions from the code blocks. C and C++ are exceptions, for they require pointers access the memory.

Moving on the Wheels of Giants

The topic Files and Serialization (Marshalling) marked the end of the main fundamental programming concepts, that can be applied to any (or almost every) paradigm. From this topic on, it is time to start exploring complementary programming resources. In fact, complementary resources often extends or provide additional features to the fundamental concepts.

For instance, the creation of subroutines (such as functions or procedures) or records (or classes) allow creating reusable code. A subroutine can be called many times in a program; similarly, one can declare multiple instances of variables of the type of a record.

Thus, when one creates reusable code, why she/he should not use it in multiple projects? In fact, it is possible to copy definitions of subroutines and records exiting in source code files to new programs. Some examples have adopted this approach over the previous topics (for instance, to reuse Lua subroutines or to encode binary files).

Nevertheless, copy and paste code snippets is not a good approach for software reuse. As have been mentioned in subroutines and records (and in many other topics), it is rarely a good idea to duplicate code. For instance, if someone finds an implementation error in a duplicated code, she/he will need to fix the issue in all files that used the duplicated code.

A better approach consists of defining reusable source code in their own files. Thus, when one wishes to use the code, she/he can duplicate the whole file. When the code in the file changes, it suffices to update the file(s) in the projects that use it(them). An even better approach consists of keeping in a common directory (or hierarchy of directories) to reference them in all future projects. Without duplicates, every fix and improvement will benefit all projects using the feature.

Like libraries reunite artifacts (such as books, magazines and journals) containing ready-to-consult knowledge, programming libraries (libraries, hereafter) provide ready-to-use code.

Libraries, Frameworks and Engines

Some previous topics have briefly commented about the creation and use of libraries for some programming languages. For instance, the development environment configuration for the C language (which also applies to C++) has quickly mentioned libraries, link and linker, headers and directories of headers, build systems, and cross-compilation (compilation performed in a platform to generate code for another platform). Now, it is worth to expand the concept of libraries.

A self-contained file with reusable code font defines a library. In a simplified definition, a library is a file with definitions of constants, subroutines, records (class and data types) and global variables (or belonging to a own scope). Definitions can include the source code for the implementation or only signatures, by means of an Application Programming Interface (API).

A module is a way of distributing many files or libraries as a single package. Technically, the definition, the complexity and the resources can vary. In the simplest form, a module is a library of libraries (or a single library). In more sophisticated approaches, modules provide additional resources and information, such as versions, forms of isolation from external code and namespaces to avoid conflicts of variables, subroutines or records with a same name in the global scope. Furthermore, a Software Development Kit (SDK or devkit) can group related libraries or tools in a single package.

In particular, libraries do not define an entry point (for instance, a main() function). A library that defines an entry point is typically called a framework or an engine, depending on the chosen approach.

In a framework or engine, it is common (although not obligatory) that the entry point is predefined. The framework or engine provides a subroutine (or method or object) that must be defined with the specific code of a program. This is known as inversion of control (IoC), also called Hollywood principle. The comparison is due to the saying that, in Hollywood, an actress or an actor does not contact a studio; rather, the studio contacts the person.

In programming, the inversion of control can be implemented, for instance, using event-driven programming. A common resource in event-driven programming are subroutines called callbacks. A callback is implemented by the application's programmer with the desired code to run in his/her programs. The subroutine is called by the framework or engine at an appropriate time (instead of by the programmer), when it should run.

For instance, a callback has been previously used in JavaScript to read files. To read an entire text file, a subroutine was defined to set up onload. The subroutine defined in onload was called at an appropriate time by readAsText(), after loading the data.

let file_reader = new FileReader()
file_reader.onload = function(evento) {
    // Code for the callback onload().

    let contents = evento.target.result
    console.log(contents)
    // ...
}
file_reader.readAsText(arquivo_texto)

Event-driven programming is a common and versatile programming paradigm, which can help to implement programs with desirable qualities (such as high cohesion and low coupling). In an engine or framework, the structure usually provides specific names for important callbacks. For instance, a framework to process files could establish that the callback that receives a subroutine to process a text file that has been read should be named on_file_loaded(). Thus, an example of using the proposed framework when creating an application could be:

// Code of the application using the framework...
function on_file_loaded(contents) {
    console.log(contents)
    // ...
}

The framework or engine would implement the remainder of the code and would call the subroutine on_file_loaded() when appropriate. It assumes that the definition exists, to call it (whenever necessary) with the calculated values.

// Code of the framework...
class FileProcesser {
    constructor(on_file_loaded) {
        this.on_file_loaded = on_file_loaded
        // ...
    }

    read_file() {
        // Open file, access data, save in contents...
        let contents = "Hello, my name is Franco!"
        this.on_file_loaded(contents)
        // ...
    }
}

// Alternative implementation, with the callback as a global subroutine or in a suitable namespace:
class FileProcesser {
    read_file() {
        // Open file, access data, save in contents...
        let contents = "Hello, my name is Franco!"
        if (on_file_loaded) {
            on_file_loaded(contents)
        }
        // ...
    }
}

The application programmer does not need to know how the framework is implemented. She/he only needs to know that they must implement a subroutine for the on_file_loaded() callback.

In fact, this is what happens, for instance, in GDScript code. The code defined in subroutines such as _ready() and _init() are callbacks called by the Godot Engine to run the source code at an appropriate time. Thus, the contents of this section introduces names for some resources and concepts that have already been used in previous topics (albeit, technically, GDScript use virtual methods and polymorphism, as it can be learned by reading the C++ source code's header for the class Node). By the way, this is an important advantage of open source and free software: you can learn by reading the source code of programs and libraries that you use.

Reinvent the Wheel or Stand on the Shoulders of Giants?

Besides the default library of programming languages, there are external libraries (also called dependencies or third-party libraries). For instance, the Lua version of JSON file serialization used an external library to convert strings to the JSON format. This was necessary because Lua did not provide the feature in its standard library. Although it was possible to implement a custom solution, using the library has enabled us to focus on the problem at hand (illustrate the use of files) instead of implementing a code to convert text to JSON text format. On the other hand, as the feature is not part of the default library or the program's code, the project will not work until the acquisition of the code of the external library (hence the name dependency).

In programming, the expression "reinventing the wheel" is common, especially for its negation "one must not reinvent the wheel". The contrary of "not reinventing the wheel" is "not invented here" (NIH).

The expressions related to the idea that, normally, it is more beneficial using an existing solution to a problem implementing one, provided that such solution does already exist. In other words, often it is preferable using a library (or framework, engine, or any other variation) than creating a new one that performs the same operations. Likewise, it is typically preferable using an existing solution than creating a new one only because the existing solution "was not invented here". Naturally, there are exceptions and criteria to choose good library, as it will be discussed.

Before, though, to explore a third phrase, there is the expression standing on the shoulders of giants, which has been popularized by Isaac Newton as "if I have seen further it is by standing on the shoulders of Giants". High quality libraries allow developing code faster because it allows standing on the shoulders of giants; thus, why not paraphrase it as moving on the wheels of giants?

In fact, wheels of giants are usually preferable; though there exists situations in which it is preferable creating your own libraries. On reason is for learning. It is always valid to creating your own solution for learning purposes. On the other hand, the choice of using one's own solution is debatable if there exists better alternatives, thus requiring technical reasons and appropriate justifications. It is also debatable using libraries with questionable quality or trivial implementation just for the sake of using a library. Therefore, it is worth considering some points to decide in a well-founded way whether to adopt (or not) a library. After all, programming is part art, part science; technical decisions require analysis from the scientific part of it.

Licenses

Disclaimer. This is not legal advice. For professional or commercial use of software and library, you should request advice for lawyers that are specialicists in software.

The public existence of a library or code that solves a problem does not mean, necessarily, that one can use it. Source code is normally protected by copyright laws (authors' rights) and intellectual property (in some countries, programs may also be patented). Thus, authorization from the authors is necessary to use their code in your program. Therefore, it is essential to consider the license of the source code of a library before using it.

Some licenses are restrictive. They can require paying licensing fees for use or impose a certain license to your own program. Licenses that require payment, or conditions for use or redistribution are called proprietary licenses (they are governed by copyright). Licenses that not impose distribution conditions are governed by copyleft (though they may still impose other restrictions).

Copyleft often does not impose financial barriers for using or distributing source code, though it can have other requirements. For instance, copyleft can be strong, if it imposes that every derivative work is licenses using the same license from the code that has been used (or one that is compatible). This is also known as viral license, and it is the case of licenses such as GNU General Public License (GPL) and Affero General Public License (AGPL). The previous licenses guarantee (legally, not necessarily ethically) that every code that uses the libraries will have the same license -- that means they will be open source. If you do not want to share your source code, or if you aim to impose that people who use your code must make public their projects using your source code, it may not be desirable.

Other licenses have weak copyleft. For weak copyleft, some licenses such as GNU Lesser General Public License (LGPL, if used with dynamic link; otherwise, it is equivalent to GPL) and Mozilla Public License (MPL) require that the derivative solution use the same license. Others, also called permissive licenses, can be used by attribution (citation of the license and inclusion of the license, of its terms and authors), provided that their original license is not changed. Examples of such licenses are MIT (or X11), Berkeley Software Distribution (BSD) e Apache.

There exists many other licenses for the previous categories. There are also projects licenses with multiple licenses (such as dual-licensing). For instance, the library may be free for personal use, but it must require payment for commercial use (or from a predetermined income). Other projects use licenses such as Unlicense, with the goal of marking their work as public domain (instead of imposing copyright or copyleft).

Thus, using an external library can impose licensing restriction for your own program. In particular, you should check whether the licenses of different libraries are compatible with each other and with the goals of your program.

In short, sometimes there exists an excellent library available, though its license might be impracticable for your goals. Especially if you wish to make commercial use of a library or of a program developed using a library, it is necessary, thus, to pay attention to the choice to avoid legal problems in the future.

Quality and Maturity

As a rule of thumb (although there are exceptions), the quality of the standard library of a programming language tends to be good (or, at least acceptable). However, the quality of an external library varies.

Some libraries are new, unstable, and/or experimental. They may not be recommended to projects that require stability. In particular, APIs of new libraries often change, which requires changing the source code a project using them. Some libraries have been good in the past, though they are currently outdated or without maintainers. This makes their adoption tricky; they may solve the problem today, though there are no guarantees that they will still work in the future. Other libraries are robust, stable, tested (preferably with an automated test suite), maintained, well-documented, and used by thousands of projects. In particular, the availability of high-quality documentation is desirable, for it is important for effective use of the library.

Thus, the maturity and future prospect of a library are relevant criteria to motivate (or hinder) its adoption. Before adopting a library, it is worth analyzing its viability at the medium and long-term. This is particularly important if your project is long or will be maintained for years. If you use a library that stops being updated or cease to exist, you will probably have problems in the future.

In this regard, open source libraries have the inherent advantage of source code availability. In the worst scenario, it is possible to fork the original project and maintain it. Furthermore, if you find problems, you can eventually use your software development skills to solve them, and send your patches to the original project. As a result, you and other people will be able to benefit from fixes and improvements in a collaborative way. In fact, a participatory and active community of collaborators is a good signal when one considers adopting an external library.

On the other hand, open source libraries do not provide warranty. If warranties are important for a project, it can be necessary opting to proprietary solutions that do provide them.

Security

In a last analysis, you can only trust code that you have read and completely understand, or that have been audited (and with a copy that has been acquired without modifications since the inspection). For greater transparency and honesty, at times you cannot even completely trust yourself. Few organizations follow formal methodologies that guarantee the correctness of a specification or a program. In general, software is implemented and (hopefully) tested. The guarantee is the tests; in other words, one can tell if the system has errors, though she/he cannot affirm whether is correct.

Virtually every minimally complex software has problems. They might be unknown, though they possibly exist. This applies both to your code and to every library that it uses (the language's standard and external ones).

In this sense, using an external library provides advantages and disadvantages. One import advantage is that popular libraries are used and tested by thousands of projects. Thus, they tend to work well for common and frequent use cases.

Conversely, uncommon use cases tend to be less tested and, thus, can have issues. In particular, security ones.

Thus, a popular and high-quality external library tend to be safer than one's own library due to the frequency of use. However, every use assumes good faith of the development team of the library, as well as the origin used to acquire the files. Even if the code of the library is relatively secure and correct, the acquired files may be compromised. This can be particularly aggravated in Web systems that use libraries hosted remotely, outside your control. For instance, JavaScript's npm package manager has had some issues and has been a target of attacks over the years. In particular, in January 2022, the maintainer of a popular JavaScript library called colors changed its implementation to use an infinite loop, which has compromised many websites that were using the most up-to-date version of the library.

Evidently, development teams that used the latest version of the library without checks and tests share the responsibility. The point, though, is that software development commonly assumes good faith of the involved parts. In other words, trust.

Programming requires care and responsibility. As usual, my recommendation to develop software is to suspect everything and everyone (yourself and myself included). Whenever you use external code, it is wise to inspect the code to verify if it truly does what it claims to do. Except if you have access to the source code of a library, and you inspect every change, there are no guarantees about its safety. For total control of a solution, it may be necessary to avoid using external code.

Size and Dependencies Required by the Library

At times, there are high quality libraries that solve a problem. It has a suitable license, the project is viable and mature, the code is stable and (from what is known) safe to use. It seems a perfect choice.

Unfortunately, there is an additional fact that can be relevant for the adoption of a library: the (system's) resources that it requires. If your project has hardware requirements, the size of a library can be relevant. There are libraries with tens of millions of lines of code; your project may have a few hundreds. In other words, you have a program that could have potentially be light, though it becomes heavy due to use d library (which known as bloat). This can happen both for the size of project (that could have had a few kilobytes, though it can reach hundreds of megabytes due to the chosen library), and for the usage of computational resources during use. Both situations can be common in languages such as JavaScript.

Furthermore, the same way that your project can have external dependencies, a library can also have them. In other words, the project may depend on a library that depends on other libraries (that depends on other libraries...). In particular, this can cause conflict problems if a dependency needs a specific version of a library, though other dependencies need another version of the same library. Thus, not every library is compatible with every other library.

Thus, it can be useful to opt for libraries with few dependencies (or ideally, none other than the standard library) can be a good criterion for selecting a library.

Standing on the Shoulders of Giants, with Parachutes for Secure Emergency Exits

The previous subsections may motivate the "not invented here" approach. Every choice in computing has trade-offs. As always, it is worth taking informed and rational decisions for a choice.

However, software development requires standing on the shoulders of giants. With good programming practices, it is possible to abstract the use of an external library or your own using a custom API that you create yourself. Thus, it is possible to start a project using an external library and change it in the future with minimal modifications to the source code of the project. This open possibilities such as switching to an external library or create your own implementation in the future. Alternatively, you can start with your own implementation and switch to an external library if/when necessary.

In other words, it is worth standing over the shoulders of giants, as well it is prudent creating protections not to fall in cases of instabilities or when the giant becomes (or shows itself) unsuitable to the task at hand. One can think development as if composed by blocks, known as components. The created interfaces define the sockets to fit pieces; the components are the pieces of blocks. When sockets are created, one can add a piece to her/his work; thus, a solution with low coupling and high cohesion. Subroutines and records are two strategies that can be explored for this purpose. In functional programming languages, it is possible to abstract the use with high-order functions such as callbacks. In object-oriented programming, one can use polymorphism. These techniques will be shown near the end of this topic. In languages without any of the previous resources, one can use conditional structures as an alternative (though a limited one).

Your Very Own Libraries

Except in examples for JavaScript (which often also required HTML files), most projects from past topics defined the source code in a single file. Henceforth, libraries will allow splitting a project into multiple files. Besides making it easier to reuse source code among different projects, this allows grouping code according to their functionalities, possibly improving the project's maintainability.

To illustrate how to create and use libraries, the first example for each language will implement a single procedure. Next, the second example will implement a record and some operations. The record will be bidimensional (two-dimensional or 2D) geometric point.

A point has two values (coordinates): , called abscissa, and , called ordinate, which are numbers (which the examples will assume real numbers). Some operations with the points and include:

  1. Sum:

  2. Difference:

  3. Distance:

Thus, operations between points use the respective coordinates for each point. The previous operations are common in computer graphics (CG) and simulations (for instance, for Mathematics, Physics, and digital games). To anticipate future topics (more interactive and multimedia) and a first simulation in this topic, it is worth defining them here -- in the reusable form of a library.

Creating and Using Libraries in JavaScript

There are two main ways of using libraries in JavaScript, depending on the target:

  1. JavaScript for browser interpreters;
  2. JavaScript for command line interpreters, such as SpiderMonkey or Node.js.

All examples from previous topics assume the use of browsers. Although this topic will use Node.js later, it is convenient starting with browsers. For JavaScript for browsers, three files can be defined:

  1. A file with the library definition (for instance, library.js);
  2. The file that will use the library (for instance, project.js);
  3. A page for the project (for instance, page.html).
export function hello() {
    console.log("Hello, my name is Franco!")
}
import { hello } from "./library.js"

hello()
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script src="./project.js" type="module"></script>
  </body>
</html>

The file project.js illustrates using the library library.js. It is also possible loading the entire code of the library in a variable, using it as a reference to access subroutines, classes and variables defined in the library.

import * as library from "./library.js"

library.hello()

In both cases, the operation of loading the library uses import (documentation). Libraries defined in the previous form are called modules (documentation), which are the modern way of defining JavaScript libraries. If one wishes to explore side effects of a library, she/he can use import "/path/to/module". Some style guides suggest using the extension .mjs instead of .js, to make it explicit that the file defines a module. This can be useful because there exists a second way of creating libraries in the languages, which is more traditional: one can create global definitions in a file and interpret the file with the library before running the file with the project's code.

Regardless, it is important noticing that the previous code will not work in a browser directly. For safety, a page in a browser cannot access files of the machine without an action that was started by the user (as it has previously happened to read files). To use the code, you will need to start a Web server, which will serve (hence the name) requested files to the browser using an Internet protocol such as Hypertext Transfer Protocol (HTTP) in a specific port.

If the Python is installed in your machine, a simple way to start a local Web server is navigating to the directory of the files, open a command prompt and use the following command:

python -m http.server 8080 --bind 127.0.0.1 --directory ./

The value 8080 defines the port used for communication. The command will start Python's http.server from the Python module with the same name (documentation). It is a basic server that can be used for development purposes (which means it is not an option as a production server).

Next, you will be able to access the page in a browser using the address: http://localhost:8080/page.html. It is important to use the port 8080 in the address. If it is omitted, the browser will use the port 443 for the HTTPS protocol or 80 for the HTTP protocol. For instance, https://www.francogarcia.com:443/ and https://www.francogarcia.com/ are equivalent addresses, and so are http://www.francogarcia.com:80/ and http://www.francogarcia.com/. The value of localhost is typically the Internet Protocol (IP) address version 4 (IPv4) 127.0.0.1 or version 6 (IPv6) ::1. In the Internet, every domain name is translated to a IP address by a Domain Name System (DNS).

When one uses as Web server, she/he can reference the root of the server using a forward slash (/). Thus, the HTML could be rewritten as:

<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script src="/project.js" type="module"></script>
  </body>
</html>

In the original code, the dot (.) references the current directory in the command prompt. As the Web server has been started in the current directory (./) as the server root, the pats are equivalent. If you are confused about file paths, it is worth consulting File Systems: Files, Directories (Folders) and Paths.

Additionally, the tag script in script must define type="module", to inform that the script can use JavaScript modules. If the value is omitted, import will fail. If, for any reason, one cannot use modules, it is possible to load JavaScript code as global values and use them. To do this, one can use the traditional approach for libraries in JavaScript as a fallback. The next example illustrates such use, embedding JavaScript code in an HTML <script> tag:

<script type="module">
  // JavaScript code using modules.
  // It is run by browsers that support modules,
  // and it is ignored by browsers that do not support them.

  import { hello } from "./library.js"

  // ...
</script>

<script nomodule>
  // JavaScript code without modules.
  // It is ignored  by browsers that support modules,
  // and it is run by browsers that do not support them.

  function hello() {
      console.log("Hello, my name is Franco!")
  }

  // ...
</script>

The next examples assume the use of a modern and up-to-date browser; therefore, nomodule will not be provided as a fallback. For the point example, one can create three files:

  1. A file with the definition of the library (in this case, point.js);
  2. The file that will use the library (for instance, main.js);
  3. A page to use the previous file (this time index.html).
class Point {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
}

const ORIGIN = new Point(0, 0)

function add(point1, point2) {
    let result = new Point(point1.x + point2.x,
                           point1.y + point2.y)

    return result
}

function subtract(point1, point2) {
    let result = new Point(point1.x - point2.x,
                           point1.y - point2.y)

    return result
}

function distance(point1, point2) {
    let dx = point1.x - point2.x
    let dy = point1.y - point2.y
    let result = Math.sqrt(dx * dx + dy * dy)

    return result
}

function write(point) {
    // console.log("(" + point.x + ", " + point.y + ")")
    console.log(`(${point.x}, ${point.y})`)
}

export {
    Point,
    ORIGIN,
    add,
    subtract,
    distance,
    write
}
// To use with Node.js:
// let point = require("./point")
// To use as a module:
import * as point from "./point.js"

let point1 = new point.Point(1, 2)
point.write(point1)
let point2 = new point.Point(4, 6)
point.write(point2)

console.log(point.distance(point1, point2))

let summed = point.add(point1, point2)
console.log(point.distance(summed, point.ORIGIN))

let subtracted = point.subtract(point1, point2)
console.log(point.distance(subtracted, point.ORIGIN))
<!DOCTYPE html>
<html lang="en-US">

  <head>
    <meta charset="utf-8">
    <title>Libraries in JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body>
    <main>
      <script src="./main.js" type="module"></script>
    </main>
  </body>

</html>

To use the project:

python -m http.server 8080 --bind 127.0.0.1 --directory ./

Next, you will be able to access the page in a browser using the address: http://localhost:8080/index.html. As the page is called index.html, you can also use the address http://localhost:8080/.

The implementation of the record and the subroutines should not be surprising. In write(), the syntax `${x}` allows writing the value of a variable as a string. In this case, it is equivalent to "(" + point.x + ", " + point.y + ")".

The file of the library point.js define records, constants and subroutines, though not file is executed automatically. The parts that will be provided by the library are exported using export. It is also possible to export the definitions directly by prefixing the declarations with export.

Similarly, if one does not want to use a prefix, she/he can use destructuring to load only the desired resources from the library.

import { Point, distance, add, subtract, write, ORIGIN } from "./point.js"

let point1 = new Point(1, 2)
write(point1)
let point2 = new Point(4, 6)
write(point2)

console.log(distance(point1, point2))

let summed = add(point1, point2)
console.log(distance(summed, ORIGIN))

let subtracted = subtract(point1, point2)
console.log(distance(subtracted, ORIGIN))

In the modified main.js file, each variable will be assigned the value with the same name declared in point.js.

To avoid conflicts in namespaces, one can define an alias to name the imported resource.

import * as p from "./point.js"
let point1 = new p.Point(1, 2)

import { write as e } from "./point.js"
e(point1)

The names p and e can be modified according to one's needs.

Finally, one can define a value as the default export using export default.

// point.js
// ...

export {
    Point,
    ORIGIN,
    add,
    subtract,
    distance,
    write
}

export default {
    Point,
    ORIGIN,
    add,
    subtract,
    distance,
    write
}

The modification allows to change the import as follows.

import ReceivesDefault, { write } from "./point.js"
let point1 = new ReceivesDefault.Point(1, 2)
ReceivesDefault.write(point1)
point1.x = 100
write(point1)

This time, ReceivesDefault is assigned the value of export default, while write works the same way as previously.

Finally, before using JavaScript libraries online, it is a common practice to minify (minimize the size) of the file. The goal is reducing the size of file served by the Web server to accelerate the page loading by browsers (smaller files mean less data for download; consequently, less time to acquire the file). Popular options to the tools called minifiers include UglifyJS, Closure Compiler and Esmangle.

In short, now you can split your projects in multiple files. The use of local libraries in JavaScript is more complicated as it requires a Web server; the other languages only will need the files with source code.

Creating and Using Libraries in Python

The creation of a library in Python is similar to JavaScript, though it is simpler, for it does not require a Web server. All used files can be local, stored in the file system of the machine.

The first example has two files: library.py, with the code of the library, and project.py, that uses the code implemented by the library.

def hello():
    print("Hello, my name is Franco!")
from library import hello

hello()

One may notice that, as she/he has used modules from the standard library in previous topics using import (documentation), the process of using her/his own libraries is similar. To load the code, she/he uses import library_filename. If she/he wishes to load a particular class, subroutine or variable, she/he can use from library_filename import resource_name. If she/he imports the entire library, it suffices to prefix each call with library_filename.

import library

library.hello()

To run a project, one should use python project.py, on which project.py is the file defining the entry point of the program, usually called main. If one is using an IDE such as Thonny, she/he can run the main file (in this case, project.py). In some IDEs, however, it may be necessary to choose a file as the main script or define a main() subroutine as follows.

import library

def main():
    library.hello()

if (__name__ == "__main__"):
    main()

The implementation for the point is similar.

import math
from typing import Final

class Point:
   def __init__(self, x, y):
        self.x = x
        self.y = y

ORIGIN: Final = Point(0, 0)

def add(point1, point2):
    result = Point(point1.x + point2.x,
                   point1.y + point2.y)

    return result

def subtract(point1, point2):
    result = Point(point1.x - point2.x,
                   point1.y - point2.y)

    return result

def distance(point1, point2):
    dx = point1.x - point2.x
    dy = point1.y - point2.y
    result = math.sqrt(dx * dx + dy * dy)

    return result

def write(point):
    # print("(" + str(point.x) + ", " + str(point.y) + ")")
    print(f"({point.x}, {point.y})")
import point

point1 = point.Point(1, 2)
point.write(point1)
point2 = point.Point(4, 6)
point.write(point2)

print(point.distance(point1, point2))

summed = point.add(point1, point2)
print(point.distance(summed, point.ORIGIN))

subtracted = point.subtract(point1, point2)
print(point.distance(subtracted, point.ORIGIN))

To avoid conflicts of names of modules or subroutines, libraries can be imported with a different name. To do this, one uses as as part of the import command.

import point as p

point1 = p.Point(1, 2)
p.write(point1)

# For a indentifier in particular.
from point import write as e
e(point1)

The names p and e can be modified as required. This makes it possible to avoid name conflicts in a namespace.

Creating and Using Libraries in Lua

There are two main ways of creating libraries in Lua. The first way consists of declaring all variables and subroutines in the local namespace, using local. In the end of the library's file, a table with the definitions that will be exported should be returned.

local function hello()
    print("Hello, my name is Franco!")
end

return {
    hello = hello
}

-- Another way of writing it:
-- local library = {}

-- function library.hello()
--     print("Hello, my name is Franco!")
-- end

-- return library
local library = require("library")
-- It is also possible to write code such as:
-- local library = require "library"
-- The reason is that, in some cases, Lua allows making calls to
-- subroutines without parentheses.

library.hello()

-- If you wish to omit the variable, you can store a reference to the
-- function into a variable.
local hello = library.hello
hello()

To load the library using this approach, one uses require() (documentation). To run the code, she/he uses lua project.lua in the command line, or runs the file project.lua using an IDE such as ZeroBrane Studio.

The second approach is a variation of the first, using the global namespace.

local library = {}
_G.library = library

function library.hello()
    print("Hello, my name is Franco!")
end
require("library")
-- or dofile("library.lua")

library.hello()

In this case, library is inserted into _G (documentation). G is the default table of global variables used by Lua.

The third form consists of omitting the use of local from the library definitions, to create global variables. Next, one loads the code with dofile() (documentation), which executes code from a file.

function hello()
    print("Hello, my name is Franco!")
end
dofile("library.lua")

hello()

The first approach is preferable for libraries, for it avoids problems of overwriting names in the global namespace. As one can store functions in variables, it suffices to assign a function to a variable if she/he wants to create an alias. Still, the second and third approaches can be useful in projects using Lua as an embedded language in another one, with intention of changing global variables for scripts. There are other approaches, albeit these three are traditional ones.

As this is a topic about libraries, the point implementation uses the first approach. For a variation, it uses the alternative form described as a comment.

local point = {}

point.Point = {
    x = 0,
    y = 0
}

function point.Point:new(object, x, y)
    object = object or {}

    setmetatable(object, self)
    self.__index = self

    object.x = x or 0
    object.y = y or 0

    return object
end

point.ORIGIN = point.Point:new(nil, 0, 0)

function point.add(point1, point2)
    local result = point.Point:new(nil,
                                   point1.x + point2.x,
                                   point1.y + point2.y)

    return result
end

function point.subtract(point1, point2)
    local result = point.Point:new(nil,
                                   point1.x - point2.x,
                                   point1.y - point2.y)

    return result
end

function point.distance(point1, point2)
    local dx = point1.x - point2.x
    local dy = point1.y - point2.y
    local result = math.sqrt(dx * dx + dy * dy)

    return result
end

function point.write(point)
    print("(" .. point.x .. ", " .. point.y .. ")")
end

return point
local point = require "point"

local point1 = point.Point:new(nil, 1, 2)
point.write(point1)
local point2 = point.Point:new(nil, 4, 6)
point.write(point2)

print(point.distance(point1, point2))

local summed = point.add(point1, point2)
print(point.distance(summed, point.ORIGIN))

local subtracted = point.subtract(point1, point2)
print(point.distance(subtracted, point.ORIGIN))

The example follows one of the approaches described in records used for creating objects in Lua. One could use any other approach described in that topic (such, for instance, define a function create_point()).

Creating and Using Libraries in Godot GDScript

The creation of libraries in GDScript is similar to Python and Lua. There are two main approaches: the first requires instancing an object; the second use static methods for subroutines.

In the first approach, a file defines the library (for instance, library.gd), another file defines the program (for instance, project.gd).

extends Node

func hello():
    print("Hello, my name is Franco!")
extends Node

var library = preload("res://library.gd").new()

func _ready():
    library.hello()

To load a library, one can use preload() (documentation) or load() (documentation). If one knows the desired file at implementation time, preload() is preferable, because it optimizes load times during the interpretation (parsing) of the file, instead of at run-time. The res:// prefix refers to the Godot's project root. The base direction is the one that contains project.godot (or engine.cfg, in older versions). Every path must be absolute in relation to the base directory. A simple way to retrieve the path to a file is using the file system panel of the engine (the panel labeled FileSystem, not an external file manager). When one right-clicks a file on the panel, one of the options in the context menu is to copy the filepath (shortcut: Ctrl Shift C). It is also possible to click in a file in the panel and drag it to the embedded text editor; the correct filepath will be inserted into the document.

The second approach is similar, with small differences.

extends Node
class_name Library

static func hello():
    print("Hello, my name is Franco!")
extends Node

func _ready():
    Library.hello()

This time, the library file defines a name for the library using class_name. The name serves as a global reference to the class defined in the file. If one defines subroutines as static methods, they can be used as class methods instead of object methods. For the example, this can be performed as Library.hello(). In other words, the chosen name ('Library', in this case) works as a global variable to represent the class that was defined in the file. This time, the library's file defines a name for the library using class_name. Thus, the chosen must be unique in the project.

The implementation for the point is similar; it can use any approach. If one picks the second, she/he must notice that it is not allowed to define class_name as Point, because the name of the internal class is already Point.

extends Node

class Point:
    var x
    var y

    func _init(x, y):
        self.x = x
        self.y = y

# <https://github.com/godotengine/godot/issues/33531>
# const ORIGIN = Point.new(0, 0)
static func ORIGIN():
    return Point.new(0, 0)

static func add(point1, point2):
    var result = Point.new(point1.x + point2.x,
                              point1.y + point2.y)

    return result

static func subtract(point1, point2):
    var result = Point.new(point1.x - point2.x,
                              point1.y - point2.y)

    return result

static func distance(point1, point2):
    var dx = point1.x - point2.x
    var dy = point1.y - point2.y
    var result = sqrt(dx * dx + dy * dy)

    return result

static func write(point):
    print("(" + str(point.x) + ", " + str(point.y) + ")")
extends Node

var point = preload("res://point.gd").new()

func _ready():
    var point1 = point.Point.new(1, 2)
    point.write(point1)
    var point2 = point.Point.new(4, 6)
    point.write(point2)

    print(point.distance(point1, point2))

    var summed = point.add(point1, point2)
    # print(point.distance(summed, point.ORIGIN))
    print(point.distance(summed, point.ORIGIN()))

    var subtracted = point.subtract(point1, point2)
    # print(point.distance(subtracted, point.ORIGIN))
    print(point.distance(subtracted, point.ORIGIN()))

When this example was originally written, there existed a limitation to create constants as instances of a class (reference). As an alternative, the implementation provides a static method ORIGIN() to generate the expected value for the constant ORIGIN. Although the solution is not ideal and has lower performance than using a static constant, it achieves a similar result.

Sharing Libraries

After creating a library, one can use it in many projects. The simplest way to do that consists of copying the generated library file(s) to the directory of a new project and import it(them). The term was used in the plural because a library can have many source code files (something which is particularly useful to large and complex libraries).

However, the duplication is not the best way to use a library. The reason is that, if one changes the code of the library, she/he will have to update all the files that were duplicated in the other projects.

A better option consists of defining a directory for the library and import the same files in all projects. In particular, it is better defining an environment variable that using an absolute path. Environments variables have been briefly mentioned in Program Setup.

Furthermore, if you create a library, it can be useful for other people other than yourself. Thus, you may opt to share it. A way to share a library with other people consists of hosting the code in a Web repository. In this case, you will use a source-control management (SCM) system, such as git, Mercurial (hg) or Subversion (svn), and a service to host the code. Popular options include:

As of the moment of writing this topic, GitHub is the most popular choice. SourceForge is the oldest option from the four. Although it is not as popular as it has been in the past, it is still a valid option. The four options are popular, and it is worth knowing them (even if only by name).

Personally, I prefer GitLab and BitBucket. Most of my repositories are private instead of public; in the past, GitLab and BitBucket offered better free plans for private repositories than GitHub. In comparison, GitHub had only provided free plans for public repositories. Although this has since changed, I still use GitLab as my main service for hosting source code.

Alternatively, you can host the code in a server or page that is yours. Regardless of case, remember to pick a license of your preference for your library. A code without license means that all rights are reserved to the author; thus, other people cannot use it (legally).

Dependencies: Using External Libraries

You can share your own libraries with other people; likewise, you can use libraries created by other people. In fact, the process to use external libraries is similar to using your own. To use any library, you should obtain the file with the source code (or a header and the compiled library) and import it one (or more) file(s) of your project. In compiled languages, it may also be possible to obtain a header with the library's API and a binary file with the compiled code, which must be glued to your program using a linker. This has been previously commented in the development environment configuration for the C and C++ languages.

In current times, the main source of library files is the Internet. As a result, this time it will be easier using external libraries with JavaScript for browsers than in other languages (or even in JavaScript for the command line). The reason is simply because the browser mediates the access to the Internet. Thus, it makes it trivial to obtain remote files for use in HTML, JavaScript and CSS.

In other languages (and JavaScript for the command line), the process can be manual or automated by additional tools. The manual process is relatively similar in most programming languages. To use an external library, one must perform the following steps:

  1. Acquire the source code of the library.

    To do this, one usually access the official website of the library, search for the download section, and download the required file(s).

    In Linux distributions, there are also development packages to install popular libraries to several programming languages. This makes it possible to install a library a single time and use it for all programs that depends on it.

    In compiled programming languages, it is also common to obtain the headers for the library and a file with the compiled, ready-to-use code. Whenever possible, this is often recommended.

  2. The acquired files are copied to the directory (folder) or subdirectory (sub-folder) of (your) project.

    It is a good practice to create subdirectories for dependencies and reference them using relative paths. The division has the immediate advantage of separating the code of the project from the code of dependencies. Other benefits include the greater ease to add, remove or update dependencies, because the files will not be mixed together.

    For instance, one can define a directory called lib (from libraries), third-party or dependencies. Each external library can be in one subdirectory. For instance, the library "Library A" can be stored in lib/library_a/; the framework B in lib/framework_b, and so on.

  3. The desired library(ies) file(s) is(are) imported to the project. In interpreted programming languages, this if often sufficient.

    In compiled programming languages, it may be necessary to link the code of the library with code of the project to use it. In static linking, it is possible to compile the library together with the project. In dynamic link, it is necessary to pre-compile the library before using it. It is always interesting to pre-compile library code, to reduce the total time of compilation. This is due to the fact that, if the library code does not change, it is not necessary recompiling it to generate the same code.

    To better understand how the process works in compiled languages, you can access the page describing This has been previously commented in the development environment configuration for the C languages. The process is the same for C++, and similar (albeit simpler) in languages such as Java.

If the library depends on other libraries, it is necessary repeating the steps (1) and (2) and set the import directories (and link, for compiled languages).

Therefore, it is almost always easier to use libraries without (ideally) dependencies or with a few ones. This was the reason for choosing a library with a single file to use JSON in Lua. Another option is selecting a library that includes their own dependencies. This happens, for instance, in engines or frameworks. The disadvantage is the lesser control over included versions of libraries.

If the process seems to be repetitive, it truly is. Thus, many programming languages provide resources to automate it, by means of programs called package managers. Before describing some, JavaScript for the browser can be used as an example of how to use external libraries using the classic process.

Using External Libraries in JavaScript for Browser

To make the example simpler, the JavaScript is embedded in the HTML code, inside the <script> tag. The solution would also work with multiple files, using the src attribute of <script>.

The first version imports the library in the global scope. The file hello-franco-global.js defines two procedures: ola_franco() and hello_franco(), that write messages that are traditional in this material. You can use the address in the source to retrieve the file in a browser, if you wish.

<!DOCTYPE html>
<html lang="en-US">

  <head>
    <meta charset="utf-8">
    <title>External Libraries in JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- For the local server: -->
    <!-- <script src="/hello-franco-global.js"></script> -->
    <script src="https://www.francogarcia.com/js/hello-franco-global.js"></script>
  </head>

  <body>
    <main>
      <!-- JavaScript code embedded in the HTML file. -->
      <script>
hello_franco()
      </script>
    </main>
  </body>

</html>

The first version presents the traditional way of working with libraries in JavaScript for browser. There is also a second way, which is more modern and use modules. The second version requires modern and up-to-date browsers to work. Furthermore, the chosen module cannot import other files using require() (which is specific to Node.js).

<!DOCTYPE html>
<html lang="en-US">

  <head>
    <meta charset="utf-8">
    <title>External Libraries in JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body>
    <main>
      <!-- JavaScript code embedded in the HTML file. -->
      <script type="module">
// For the local server:
// import { hello } from "/hello-franco-module.js"
import { hello } from "https://www.francogarcia.com/js/hello-franco-module.js"

hello()
      </script>
    </main>
  </body>

</html>

The library hello-franco-module.js has two procedures: ola() and hello(), which are equivalent to ola_franco() and to hello_franco(), respectively.

If you use a local serer to test the files above in the directory on which they are saved, you must access the address http://localhost:8000/filename.html. filename.html should be replaced by the of the file. This might be necessary if the browser throws errors due to differences of protocols to access the file of the library. The original file uses the HTTPS protocol (https://); localhost uses HTTP (http://); local files use file (file://).

Further Examples: jQuery, Lodash and Cowsay

One of the simplest ways to use libraries for HTML, JavaScript and CSS consists in the use of a Content Delivery Network (CDN).

For an example using CDN, the following snippet uses the library jQuery (MIT license), which is popular for front-end development for the Web. jQuery does not use modules, providing another example of loading libraries with global definitions.

The library jQuery provides instructions describing how to use it with a CDN. For the official distribution, one can access this link. It is also common to use versions hosted by Google or Microsoft, to maximize the changes that the file has been download previously and already exists in the browser's cache (potentially avoiding requiring a new download).

$(document).ready(function() {
    $("a").hover(function(event) {
        // Increase the size of the font and change the color to red when the link is focused.
        $(this).css("font-size", "50px")
        $(this).css("color", "red")
        $(this).text("Click or touch to access!")
    }, function(event) {
        // Reduce the size of the font slightly and change the color to orange when the link loses focus.
        $(this).css("font-size", "30px")
        $(this).css("color", "orange")
        $(this).text("Hey, come back here!")
    })

    $("a").click(function(event) {
        alert("Thank you very much!\nAccessing the page...")
    })
})
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <script src="https://code.jquery.com/jquery-3.6.0.slim.min.js"
            integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI="
            crossorigin="anonymous"></script>
  </head>
  <body>
    <a href="https://www.francogarcia.com/">Access the author's website</a>
    <script src="./script.js"></script>
  </body>
</html>

The different syntax for the JavaScript code is provided by jQuery. jQuery is usually imported with the $ (dollar bill) sign. jQuery also uses callbacks. The implementation in the JavaScript code changes the color, size and the message of the link when it receives (or loses) focus. Besides, when the link is clicked, it shows a message thanking the access.

For this topic about libraries, the goal was to show how to use an external library using JavaScript for browsers. If you want to learn more about jQuery, refer to the documentation.

To illustrate the use of a module, one can choose the library Lodash (MIT license). The version using modules is called lodash-es.

import * as _ from "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js"

// Examples from the main page of the library:
// <https://lodash.com/>
console.log(_.defaults({ 'a': 1 }, { 'a': 3, 'b': 2 }))
console.log(_.partition([1, 2, 3, 4], n => n % 2))

// <https://lodash.com/docs/4.17.15#cloneDeep>
let objects = [{ 'a': 1 }, { 'b': 2 }]
let deep = _.cloneDeep(objects)
console.log(deep[0] === objects[0])
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <title>External Libraries in JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <div id="contents"></div>
    <script src="./script.js" type="module"></script>
  </body>
</html>

Lodash is often imported as the _ (underscore) character. The library provides useful resources to manipulate strings and collections (such as arrays and sets). It has been mentioned previously for its method cloneDeep() in Arrays, strings, collections and data structures. cloneDeep() can make deep copies of arrays, dictionaries and other data structures.

Once again, the goal is not to introduce Loadash; it is to show how to import a module. The process to import modules in modern browsers is simple, though it still is restricted. For instance:

  • It will fail if the library implementation has code that is specific for the command line (such as for Node.js);
  • At times, it can be necessary to load the library as a side effect;
  • It may be necessary to convert the original code to a format compatible with the browser. This can be performed, for instance, using tools such as webpack or Browserify.

For an additional example, the next snippet uses the library Cowsay (MIT license). cowsay is, originally, a command line program that draws textual art of a cow saying a message. The original code is available in this repository (GPL-3.0 license). The Wikipedia entry some examples of output from the original program.

// Strictly speaking, the module should be imported as:
// import * as cowsay from "https://cdn.jsdelivr.net/npm/cowsay@1.5.0/build/cowsay.es.min.js"
// Or:
// let {say, TUX} = await import("https://cdn.jsdelivr.net/npm/cowsay@1.5.0/build/cowsay.es.min.js")
// However, there is an issue with paths in the interal imports:
// <https://github.com/piuccio/cowsay/issues/48>

// Thus, it had to be loaded as a side-effect:
import "https://cdn.jsdelivr.net/npm/cowsay@1.5.0/build/cowsay.umd.min.js"
// Or:
// await import("https://cdn.jsdelivr.net/npm/cowsay@1.5.0/build/cowsay.umd.min.js")

let message = prompt("Write a message", "Hello, my name is Franco!")
let result = cowsay.say({
    text: message,
    // cow: cowsay.TUX,
    eyes: "oO",
    tongue: "U "
})
console.log(result)

// <https://francogarcia.com/en/blog/development-environments-javascript/>
function add_element(value, element_name = "p") {
    const parent = document.getElementById("contents")
    const new_element = document.createElement(element_name)
    new_element.innerHTML = value
    parent.appendChild(new_element)
}
add_element(result.replace(/\\n/g, "<br/>"), "pre")
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <title>External Libraries in JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <div id="contents"></div>
    <script src="./script.js" type="module"></script>
  </body>
</html>

A Web server may be required to run the example.

python -m http.server 8080 --bind 127.0.0.1 --directory ./

With a server running, you can access the example using http://localhost:8080/cowsay.html.

This time, the library comes from a CDN called jsDelivr. jsDelivr and UNPKG are options for CDNs that provide libraries hosted by npm.

The alternative implementation described in a comment uses await in the import to wait the module to load. To show the ASCII Art image generated as the output, the program uses add_element() and a regular expression to replace lines breaks in the strings (\n) by HTML line breaks (<br/>). Furthermore, the chosen tag is pre to write the result in monospaced font. This is required because the drawing assumes that all characters have the same width.

The following block provides a possible output of the program. If the content is read by a screen reader, the text will not make much sense.

 _________________________
< Hello, my name is Franco! >
 -------------------------
        \   ^__^
         \  (oO)\_______
            (__)\       )\/\
             U  ||----w |
                ||     ||

The block contains the drawing of a cow created with characters (this is known as ASCII Art). The cow says Hello, my name is Franco! in a balloon that is drawn with hyphens, underscores, and less than and greater than symbols.

There are other cowsay implementations for JavaScript. One is called cowsayjs, though it imports code using require(). Otherwise, it would be simpler to use:

import * as cowsay from "https://cdn.jsdelivr.net/npm/cowsayjs@1.0.5/lib/index.min.js"

let result = cowsay(message)
console.log(result)

Instead of using it here, it will be explored after introducing package managers.

Package Managers

Although using external libraries in JavaScript for browsers is simpler than in other languages (and JavaScript for console or terminal), modern programming languages often provide programs called package managers.

The term package manager is known by people who use the Linux operating system. Many distributions provide package managers to install and update programs (and the operating system itself) in the machine. The commands pacman in Arch Linux, apt in Debian and Ubuntu, dnf or yum in Fedora, and emerge in Gentoo are examples of managers in some traditional distributions.

JavaScript, Python e Lua provide package managers for use in the command line; some IDEs for these languages provide graphical interfaces for using the managers. Godot Engine provides an embedded package manager with graphical interface.

Using External Libraries in JavaScript (Node.js)

JavaScript with the Node.js interpreter has two good options for package managers: npm and Yarn. If the name nsm seems familiar, it is because it has been mentioned in the beginning of this page, in some commented about security in libraries.

To use any of the package managers, you will need to install them first. The official pages provide instructions in section such as Getting Started. Some people prefer npm; others prefer yarn. In general, recent versions of both are similar.

Considering npm as an example, this link provides instructions of how to install Node.js and npm. To install a package with a library (or a program) using npm, one uses the command npm install in a command line interpreter. For instance, to install cowsayjs (MIT license), she/he would use:

npm install cowsayjs

It is also possible to use npm install -g package_name, if one wants to install the package globally in the system. Personally, the author does not recommend global installs; local installations allow customizing versions of the same package in different projects with greater ease. This helps to make projects more self-contained (for instance, on which all required files are stored in a single parent directory).

Instructions to install, configure and use packages should have their own pages in the online npm catalog. For instance, this is the entry for Cowsay.

An additional advantage of package managers is the possibility to update all installed packages using npm update. Instead of replacing the files library per library, it suffices to use the command to update all the ones that are possible. Updates than result into version conflicts are ignored by the automatic process, because they require manual intervention. Although this may seem an inconvenience, it is positive: it is better to know the conflicts to be able to find the best solution for them in a project.

npm provides an additional command named npx, which allows running programs installed using npm in the command line. For instance, after installing cowsayjs, one can use the following command to write a cow saying Hello, my name is Franco! in a terminal:

npx cowsayjs Hello, my name is Franco!

For a second example, one can consider creating a project using React. As always, the documentation describes the steps. Start a command line interpreter in a directory of your choice (preferably by creating an empty folder). Then:

npx create-react-app project-name

If create-react-app (official website and documentation) is not installed, npx will ask if you wish to install it. Confirm. The setup and the project preparation probably will take some minutes. When it finishes, access the created directory (in the example, it is named project-name). To run the project in a browser, you will use npm start.

npm start

Next, you should access the address http://localhost:3000/ in a browser.

In React, a component should return the HTML code that will be displayed in a page. Instead of writing HTML code in an HTML file, the HTML code is defined in JavaScript. The special syntax with tags inside the JavaScript code is provided by React. It is called JSX (documentation).

The source code for the page is located in src/App.js. You can modify it to change the played displayed in the browser. After saving the file, the page that was opened in the browser will be refreshed automatically.

function App() {
    return (
        <div>
            <p>HTML code (written as JSX) here.</p>
            <h1>Hello, my name is Franco!</h1>
            <p>This is an HTML written inside JavaScript code using React.</p>
        </div>
  )
}

export default App

Para usar libraries, first they should be installed using npm.

npm install cowsayjs
npm start

Next, they should be imported and used in the source of the program. To include the value of a variable in the page, React provides the syntax {variable_name} for JSX code.

import { cowsay } from "cowsayjs"

function App() {
    let message = "Hello, my name is Franco!"
    let result = cowsay(message)
    console.log(result)
    result = result.replace(/\\n/g, "<br/>")

    return (
        <div>
            <p>HTML code (written as JSX) here.</p>
            <h1>Hello, my name is Franco!</h1>
            <p>This is an HTML written inside JavaScript code using React.</p>
            <pre>{result}</pre>
        </div>
  )
}

export default App

In this brief introduction, you have become a beginner in Web development using React (with a Hello, world! said by a cow). Besides the package manager, people who are interested in Web develop using Node.js should know tools such as webpack and Browserify. These tools allowing converting modules for use in Internet browsers. Although one of the previous examples show how to load a module directly, the feature is still new, with variable support. Thus, for professional activity, webpack and Browserify are still important.

Using External Libraries in Python

The process to use package manager is often similar, regardless of programming language. Thus, the concepts for JavaScript also apply for Python.

On the other hand, the Thonny IDE allows installing libraries using a graphical interface. Thus, if you do not want to use the command line yet, Thonny can be an alternative to use your first external libraries in Python.

Command Line with pip and venv

Python provides a list of official recommendations for tools. The recommended package manager is called pip. To complement pip, there is venv. venv allows installing packages locally in an isolated environment, called a virtual environment. As it has been mentioned for npm, the author prefers local environments to global ones for dependencies. To end the list, there are recommendations for creating packages and a list of packages called Python Package Index (PyPI). PyPI is a good resource to search for packages.

For this section, pip is mandatory, though venv is optional. If one does not use venv, the packages will be installed globally. Thus, if one wants to install packages locally, she/he must not use the following commands before creating the virtual environment using venv.

To install a program using Python, one uses pip install package_name in a command line interpreter. For instance, one may wish to install the libraries pandas (BSD-3-Clause license) and NumPy (also with BSD-3-Clause license).

pip install pandas
pip install numpy

If the command pip is not available, one can use python -m pip.

python -m pip install pandas
python -m pip install numpy

To test, one can define a file called script.py as follows:

import pandas as pd
import numpy as np

print("Pandas version:", pd.__version__)
print("NumPy version:", np.__version__)

In Python, it is important noticing that the name of the file that includes a module cannot have the same name of an imported module. This means that, in the previous case, the source code file cannot be called pandas.py nor numpy.py, because they are names of libraries used in the project. If one creates a file with one of these names, the execution will fail. This happens because the interpreter will try to load the created file as it was one of the libraries, resulting in a circular decency (an infinite loop for loading files). This applies to all libraries (which means that is not a particularity of pandas or numpy). For more details, one can refer to the documentation.

The library pandas is often imported as pd, and numpy is often imported as np. If you read a random Python file that uses one of the two prefixes (for instance, pd.DataFrame), it is likely that the prefix refer to the library. numpy and pandas are used, for instance, for Data Science, image processing, Artificial Intelligence, Biology, Bioinformatics, and Chemistry.

venv is used a block in the beginning and at the end of instructions. The first step is to create a virtual environment.

python -m venv virtual_environment

The previous command creates an environment called virtual_environment. Many people prefer to name the environment as venv or .venv. If you choose another name for the environment, you must replace virtual_environment by the chosen name in the next examples.

When you wish to use the environment, you will use source when the session starts and deactivate when it ends. The commands between those two that install, update or remove packages will affect only the active virtual environment. Likewise, the python interpreter will be able to use any of these packages, in addition to your own libraries, the standard library, and packages that had been installed globally.

source virtual_environment/bin/activate
# On Windows, this might be required instead:
# .\virtual_environment/Scripts/activate.bat

# pip uses...
# pip install pandas numpy
# python uses...
# python script.py

deactivate

Thus, to install pandas and numpy in a virtual environment, one uses:

# If the environment has not been created yet:
python -m venv virtual_environment

# Otherwise, one starts from here.
source virtual_environment/bin/activate
# On Windows, this might be required instead:
# .\virtual_environment/Scripts/activate.bat

pip install pandas numpy
# The interpreter can use both versions that were installed in the virtual environment.
# python script.py

deactivate

To test the environment, one can use the same script.py that was presented before. This time, the file will be run successfully only of the environment is active. In other words, it is necessary using source before running python.

source virtual_environment/bin/activate
# On Windows, this might be required instead:
# .\virtual_environment/Scripts/activate.bat

python script.py

deactivate

Naturally, it is not necessary using source and deactivate at each use. They can be used a single time per session: source when it starts, deactivate when it ends.

Graphical Interface with Thonny

To use the graphical interface to install programs with Thonny, you must access:

  1. Tools, then Manage packages;
  2. In the new interface, choose INSTALL;
  3. Next, type the name of the package in the top of the window. Press Search on PyPI;
  4. Click in the appropriate result after the search;
  5. Click on Install. If you wish to change the version, click on ....

For an example, one can search for the package pygame, that installs the library Pygame to program games in Python (LGPL license). After clicking on stall, wait the end of the setup. The result will be equivalent to running the command:

pip install pygame

After the setup ends, one can use the pygame module. To create your first simulation with graphics, you can the following code in a file named main.py. You can choose any name, other than pygame.py or typing.py.

import pygame
from typing import Final

WINDOW_WIDTH: Final = 320
WINDOW_HEIGHT: Final = 240
WINDOW_BACKGROUND_COLOR: Final = (0, 0, 0)

ball_color = (255, 0, 0)
ball_radius = 10
ball_x_position = 0
ball_y_position = 0
ball_x_speed = 100
ball_y_speed = 100

pygame.init()
window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))

last_time_ms = pygame.time.get_ticks()
finish = False
while (not finish):
    for event in pygame.event.get():
        if (event.type == pygame.QUIT):
            finish = True
        elif (event.type == pygame.KEYDOWN):
            if (event.key == pygame.K_ESCAPE):
                finish = True

    if (ball_x_position < 0):
        ball_x_position = 0
        ball_x_speed = -ball_x_speed
    elif (ball_x_position > WINDOW_WIDTH):
        ball_x_position = WINDOW_WIDTH
        ball_x_speed = -ball_x_speed

    if (ball_y_position < 0):
        ball_y_position = 0
        ball_y_speed = -ball_y_speed
    elif (ball_y_position > WINDOW_HEIGHT):
        ball_y_position = WINDOW_HEIGHT
        ball_y_speed = -ball_y_speed

    current_time_ms = pygame.time.get_ticks()
    time_delta_ms = (current_time_ms - last_time_ms) / 1000.0
    last_time_ms = current_time_ms

    ball_x_position += ball_x_speed * time_delta_ms
    ball_y_position += ball_y_speed * time_delta_ms

    window.fill(WINDOW_BACKGROUND_COLOR)
    pygame.draw.circle(window, ball_color, (ball_x_position, ball_y_position), ball_radius)
    pygame.display.update()

pygame.quit()

A note about the code is that the use of parentheses in (0, 0, 0), (255, 0, 0) and (ball_x_position, ball_y_position) is intentional. Instead of grouping, it is a tuple (documentation). A tuple is a sequence of values in which the order matters. Although it is possible to think about them as arrays (and use them as such), values in tuples are immutable (while values in arrays are mutable). This means that it is not possible to change a value in a tuple. To change a value, it is necessary to create a new tuple that copies the other values and modify the desired one.

In the code, values with X and Y positions could use the Point record that has been created. They serve the same purpose: store a point in which the drawing will start in the plane (or its center, in the case of ball).

The code creates a simple simulation of a ball (a circle) ricocheting in the window. A better implementation would consider the size of the ball when checking for the limits of position (think on how to do it). However, as in the other cases, the current goal is illustrating the use of a library.

Thonny also allows installing programs using the command line. To do it:

  1. Tools, then Open system shell...;

  2. In the window that opens with the terminal, type the command for pip. For instance:

    pip install pygame
  3. After the setup, close the terminal.

  4. Close and open Thonny. Alternatively, click on Run, then on Stop/Restart backend.

For more information, one can refer to the documentation.

Using External Libraries in Lua

After JavaScript with npm (or yarn), Python with pip, the next language is Lua. Lua has two package managers: LuaRocks and LuaDist. LuaRocks is the most up-to-date of the two. In fact, LuaDist recommends using LuaRocks.

To install a program with luarocks, one uses luarocks install package_name in a command line interpreter. For instance, to install json.lua (MIT license) used in Files and Serialization (Marshalling), she/he can use the following command:

luarocks install rxi-json-lua

Note. As the package rxi-json-lua has not been marked as supporting Lua 5.4, the previous command should fail if this is the version that you are using the interpreter. How to install and configure packages for specific versions will be commented soon.

The default installation is global. Assuming it finishes successfully, one can use require() to load the library. For global installs and without version conflicts, she/he can now use the desired library.

local json = require "rxi-json-lua"

local data = {
    name = "Franco",
    greeting = "Hello!",
    goodbye = "Goodbye!",
}
print(json.encode(data))

The name for the library and package can be searched in the LuaRocks website. For instance, this is the page for json.lua. On the other hand, it is now possible to choose options with other dependencies, such as, for instance, lua-cjson, which was the most downloaded package in the week when this topic has been written.

In other words, now it is possible to select more complex implementations, which may have more dependencies. The package manager will obtain the necessary requirements to install the chosen library.

As it has been mentioned for JavaScript and Lua, the author prefers installing packages in the project's directory, locally. For a local install, one can use:

luarocks install --tree ./dependencies/ rxi-json-lua

The previous example install the library files in the directory dependencies/. If you wish to specify a Lua version, you can pass it as a parameter. For instance, the definition of the json.lua library does not include support for Lua 5.4 (although it is compatible). To get a version of the library that is compatible with Lua 5.3, one can use:

luarocks install --tree ./dependencies/ --lua-version 5.3  rxi-json-lua

Next, it is necessary adjusting the library directories used by Lua for require(). This can be achieved by modifying the value of two variables: package.path (documentation) and package.cpath (documentation). To perform the adjustments, it suffices to concatenate the new directories to the previous variables, following the expected format.

local LIB = "dependencies/"
for _, version in ipairs({"5.3", "5.4"}) do
    package.path = LIB .. "/share/lua/" .. version .. "/?.lua;" .. package.path
    package.cpath = LIB .. "/lib/lua/" .. version .. "/?.so;" .. package.cpath
end

print("Path:", package.path)
print("CPath:", package.cpath)

The table defines are desired Lua versions. For instance, if one wishes to use values only for the 5.1 version, she/he could modify the value to {"5.1"}. The previous code must be run a single time, before the first require() that will need a library that has been installed locally by luarocks.

-- Setting the paths for the dependencies installed by LuaRocks in the directory
-- chosen as the base path.
local LIB = "dependencies/"
for _, version in ipairs({"5.3", "5.4"}) do
    package.path = LIB .. "/share/lua/" .. version .. "/?.lua;" .. package.path
    package.cpath = LIB .. "/lib/lua/" .. version .. "/?.so;" .. package.cpath
end

-- Beginning of the project's code.
local json = require "rxi-json-lua"

local data = {
    name = "Franco",
    greeting = "Hello!",
    goodbye = "Goodbye!",
}
print(json.encode(data))

Alternatively, one can configure environment variables and use a special loader provided by luarocks . The details for this setup are provided in the documentation.

With the directory configuration for package, one can install and use package from luarocks with more recent versions of Lua, provided that they are compatible with the newer version, even if the package does not make such compatibility explicit in its properties.

Using External Libraries in GDScript

Unlike JavaScript, Python and GDScript, Godot Engine's package manager has a graphical interface. The content is available in the Godot Asset Library. In general, assets are game resources, referring to images, sounds, text and other resources that are part of the content of the game. Some definitions also include scripts as assets.

Thus, content from the Asset Library often includes files that are not exclusively source code ones. In fact, many assets provide a combination of scripts and scenes, which supports configuration using the graphical editor (instead of a text editor). Some assets can be plug-ins for the engine's editor (instead of source code libraries).

To install a library (or plug-in, scene, or any other resource) from the Asset Library, you must:

  1. Click on AssetLib. The tab is next to 2D, 3D and Script, in the central, top part of the editor;

  2. Search for a resource in the search field. For instance, you can search for math and choose Extra Math for GDScript (Unlicense license);

  3. To install, click on the result's name or icon. Next, select Download;

  4. When asked, confirm the installation using Install.

    The confirmation panel shows the directory in which the files will be saved. By the default, it will be a local directory to the project, following the pattern res://addons/asset_name/.

The way to use the content will depend on what has been installed. In the case of Extra math for GDScript, it is, in fact, a source code library.

extends Node

func _ready():
    print(ExtraMath.EULER)
    print(ExtraMath.LN2)
    print(ExtraMath.get_vectors_rotation(Vector3(1, 2, 3), Vector3(2, 3, 4)))

Other similar resource to libraries are test frameworks. To identify some, you can search for test. A possible example is the Godot Unit Test (GUT; MIT license), for unity tests, which were previously commented in Subroutines (Functions and Procedures).

GUT introduces a new panel to the editor. To use it, it is necessary activating it first (as described in the documentation). To do this:

  1. Access the Project menu;
  2. Choose Project Settings;
  3. Select the tab Plugins;
  4. Enable GUT, checking the box status with Enable.

After enabling it, a new option will appear in the bottom panel. Next to Output, Debugger, Audio and Animation, there will be a new option called GUT.

Now it is possible to define unit tests using GUT. To do this, you should create a directory with tests for the project. Either use the default name tests/ or pick another one (such as testes/ in Portuguese). On the right side of the panel, configure directories that include tests. For instance, in Directory 0, write res://testes/ if using a custom directory named testes/. You can also select another prefix for names of test files. The default is test_; for a Portuguese name, one could pick teste_.

You can now create the tests. In the chosen tests' directory, create a file named teste_1 (res://testes/teste_1; if you are using the default prefix, it should be called test_1). For a real example, one could create tests for the library implementing the point that has been previously defined in point.gd.

extends "res://addons/gut/test.gd"

var point = preload("res://point.gd").new()

# Every procedure for testing should start with the prefix test_.
func test_arithmetic():
    var point1 = point.Point.new(1.0, 2.0)
    var point2 = point.Point.new(4.0, 6.0)

    # assert_eq() check whether the two first parameters have the same value.
    # Operation, expected result, description.
    assert_eq(point.distance(point1, point2), 5.0, "Distance")
    assert_eq(point.distance(point1, point1), 0.0, "Distance")
    var summed = point.add(point1, point2)
    assert_eq(summed.x, 5.0, "Sum: X")
    assert_eq(summed.y, 8.0, "Sum: Y")
    var subtracted = point.subtract(point1, point2)
    assert_eq(subtracted.x, -3.0, "Sum: X")
    assert_eq(subtracted.y, -4.0, "Sum: Y")

The example is using assert_eq(). Nevertheless, GUT provides other types of comparison; thus, it is worth referring to its documentation.

To run the tests, use Run all in the GUT panel. The files in the directories will be run; their results will be compared to the expected ones. Tests that do not match expected values will be printed. In this case, there are two possibilities:

  1. The tested implementation is wrong and must be fixed;
  2. The test is wrong and must be fixed.

Regardless of the case, the procedure is the same: check, fix, and re-run the tests.

For more information about the Asset Library, one can refer to its documentation.

Techniques for Using Libraries and Dependencies

This section presents more advanced techniques to sophisticate and improve the use of libraries.

Abstraction by Interfaces (APIs) and Programming By Contract

The creation of interfaces to abstract subroutines and data types is a good programming practice. In this context, interfaces are generic signatures for subroutines; interfaces for data types are the signatures of class' methods (or of subroutines that manipulate records). The goal is making the use of a subroutine or class depend only on knowing the signature, without requiring knowing implementation details. An immediate benefit of the practice is the possibility of changing the implementation of a subroutine without compromising code that calls it.

When your work with libraries (your own or external), this is even more useful. Although it is possible to use the signature provided by the library's API (or global variables, methods, etc.), one can define an internal API to be used in the program. In other words, instead of calling a subroutine directly, one creates her/his own subroutine which calls the library's subroutine. The used library becomes a detail; it can be replaced with ease. This recalls the title of one of the subsections of this topic: standing on the shoulders of giants, with parachutes for secure emergency exits.

The technique is important and should be part of the repertory of every good programmer. Therefore, it is worth understanding its origins and how to apply it. The goal is being able to replace implementations that perform a given processing with others that provide an equivalent result (or side effect).

To start, one can consider the following example in Python, which presents three functions that return the absolute value (modulo) of a number.

import math

print(abs(-1.23), abs(4.56), abs(0.0))
print(math.fabs(-1.23), math.fabs(4.56), math.fabs(0.0))

# Function defined in an hypothetical external library.
def calculate_absolute_value(x):
    if (x >= 0.0):
        return x

    return -x

print(calculate_absolute_value(-1.23), calculate_absolute_value(4.56), calculate_absolute_value(0.0))

The three functions have a same purpose and provide an interchangeable result. In rigor, math.fabs() always return a floating-point value (such as 0.0). As the three functions always provide the same results for real numbers, one can consider that the parameter is a real number to focus on the concept. Alternatively, the received argument can be converted to floating-point to make the result always have that type.

The problem is that, if one calls abs(), math.fabs() or calculate_absolute_value(), the program becomes dependent of the function. The program only works if a function with the name exists. If the function is defined by a library or module, it must be installed and ready for use.

The solution consists in abstracting the implementation. Instead of using any of the implementations, one can create her/his own signature, as part of the internal API of his/her program. For instance: absolute_value(x), which returns the absolute value of x (real number) as a real number. Thus: float absolute_value(float x). How the function is implemented is not relevant; it suffices knowing the effect(s) and result(s) given the parameter(s).

With the proposed interface, one can define three implementations for absolute_value() using the original snippet:

def absolute_value(x):
    # Forces a real result.
    return 1.0 * abs(x)
import math

def absolute_value(x):
    return math.fabs(x)
def absolute_value(x):
    if (x >= 0.0):
        return 1.0 * x

    return -1.0 * x

# Assuming the that calculate_absolute_value() was defined in a library:
# def absolute_value(x):
#     return calculate_absolute_value()(x)

Each previous implementation uses a design pattern called Adapter (or Wrapper or Translator). As the created adapter guarantees the same results for the three implementations, they can be used interchangeably, which means that one can choose of them to calculate the same result. Consequently, it is also possible to use of the tree library implementations without modifying the code of a program that calls absolute_value. The only difference would be the imported library wrapper.

from a import absolute_value

print(absolute_value(-1.23), absolute_value(4.56), absolute_value(0.0))
from b import absolute_value

print(absolute_value(-1.23), absolute_value(4.56), absolute_value(0.0))
from c import absolute_value

print(absolute_value(-1.23), absolute_value(4.56), absolute_value(0.0))

In fact, as the tree implementations are interchangeable, it would even be possible to define an implementation that could select between the three possible libraries.

# Library selection.
desired_library = "a"
if (desired_library == "a"):
    from a import absolute_value
elif (desired_library == "b"):
    from b import absolute_value
elif (desired_library == "c"):
    from c import absolute_value
else:
    import sys
    # Error.
    sys.exit()

# Program.
print(absolute_value(-1.23), absolute_value(4.56), absolute_value(0.0))

As the defined interface for absolute_value() is the same, the programs will work regardless of the choice (assuming that the implementations of each library are correct).

for desired_library in ["a", "b", "c"]:
    # Library selection.
    if (desired_library == "a"):
        from a import absolute_value
    elif (desired_library == "b"):
        from b import absolute_value
    elif (desired_library == "c"):
        from c import absolute_value
    else:
        import sys
        sys.exit()

    # Program.
    print(absolute_value(-1.23), absolute_value(4.56), absolute_value(0.0))

The program does not need to know the library that effectively implements the code; it suffices that the implementation follows the expected interface. This allows to, among others:

  • Swap libraries without compromising how a program works. It is possible to write one's own library or use external dependencies. It suffices mapping the names of subroutines for each library to those of the interfaces, as well as converting the parameter(s) from the interface to the data types expected by the chosen library. If more adjustments are needed, they can be adapted;
  • Write code that is specific to different platforms. For instance, one implementation for Linux, another for Windows, another for macOS, another for Android, another of OS, another for Raspberry Pi, another for a video game console. In the moment of compilation or running the program, the program (or interpreter or compiler) can select the implementation that is compatible with the platform that will run the code.
  • Swap between test (for developers) and production (for end-users) versions of a library.

This type of technique is related with a practice called Programming by Contract (or Design by Contract). The use of interfaces in general is a good programming practice and one of the pillars of an acronym known as SOLID:

  • Single-responsibility principle;
  • Open-closed principle;
  • Liskov substitution principle;
  • Interface segregation principle;
  • Dependency inversion principle.

The terms and concepts are related to Object-Oriented Programming (OPP), though the ideas are applicable to any paradigm. In particular, as JavaScript, Python and Lua (and GDScript, from the version 4 of Godot) allows using them with ease, as these languages allow changing the definition of a subroutine as any other variable.

In OOP, the implementation depends on classes, (class) interfaces, inheritance and polymorphism.

class Mathematics:
   def __init__(self):
       pass

   def absolute_value(self, x):
       raise NotImplementedError("Base class")
import mathematics

class A(mathematics.Mathematics):
    def absolute_value(self, x):
        # Forces a real result.
        return 1.0 * abs(x)
import mathematics
import math

class B(mathematics.Mathematics):
    def absolute_value(self, x):
        return math.fabs(x)
import mathematics

class C(mathematics.Mathematics):
    def absolute_value(self, x):
        if (x >= 0.0):
            return 1.0 * x

        return -1.0 * x
# Library selection.
desired_library = "a"
if (desired_library == "a"):
    import a
    mathematics = a.A()
elif (desired_library == "b"):
    import b
    mathematics = b.B()
elif (desired_library == "c"):
    import c
    mathematics = c.C()
else:
    import sys
    sys.exit()

# Program.
print(mathematics.absolute_value(-1.23), mathematics.absolute_value(4.56), mathematics.absolute_value(0.0))

In the provided examples, A, B e C are derived classes (subclasses or children classes) of Mathematics (parent class or super-class). The parent class define the common interface to use subroutines. The derived classes define (or redefine) the abstract method absolute_value with the specific implementation; this motivates the use of NotImplementedError, which is an exception thrown with raise (documentation). Constructions such as A(mathematics.Mathematics) are read as: the class A inherits the parent class mathematics.Mathematics (the first mathematics, with lowercase m, is the name of the module; the second Mathematics, with uppercase M, is the name of the class).

At use-time, the program must instance of the derived classes and use the class defined by the parent class. The parent class does not provide an implementation; it defines the contract that the children classes must implement.

Thus, the OOP version is equivalent to the first version with functions. Some programming languages allow using only one of the approaches; other allows using both; others do not allow using neither. Each tool has its particularities.

Live Coding, Live Reloading, Hot Swap and Run-Time Compilation

In the development environment configuration for the C language, it has been briefly commented about techniques such as hot swap and run-time compilation. The technique is commonly used for creating digital games and in the development of game engines.

The principle is keeping a program running, without closing it, and update it whenever an external change happens to the source code of the program. In other words, the goal is to transit modifications in the source code (as soon as the code is saved) to the process (running program). This offers an interactive and iterative development process, because it reduces the number of tasks (and the required time) between changing the source code of the project and observing the results of the program.

In compiled programming languages, hot swap requires using dynamic libraries instead of static libraries. Dynamic libraries have the extensions .dll on Windows, .so on Linux and .dylib on macOS.

In the other hand, hot swap is particularly easy to implement in interpreted programming languages, because it suffices to modify and reinterpret a region (or file) of code. In particular, the considered file can be a library.

Hot Swap in Lua

The following code uses Lua as an example.

local library = {}

function library.hello()
    print("Hello, my name is Franco!")
end

function library.operation(value)
    local result = value + 1

    return result
end

function library.finish()
    return false
end

return library
-- <https://www.francogarcia.com/en/blog/learn-programming-records/>
function sleep(time_seconds)
    local finish = tonumber(os.clock() + time_seconds);
    while (os.clock() < finish) do
        -- Wait the passage of time, wasting cycles of the processor.
    end
end

local finish = false
local number = 0
while (not finish) do
    local library = dofile("library.lua")
    library.hello()

    number = library.operation(number)
    print(number)

    sleep(0.5)

    finish = library.finish()
    if (finish) then
        print("Preparing to end the program...")
    end
end

print("End of the program")

It must be noticed that, at this moment, main.lua has an infinite loop, because finish never will be true. This is intentional.

The next step us running the program: : lua main.lua (or using an IDE). The program will write Hello, my name is Franco! and the value of number every 500 milliseconds. It is important not to close the program; it must keep running the infinite loop.

The next step is modifying the file library.lua. For instance:

local library = {}

function library.hello()
    print("Hello, my name (still) is Franco!!!")
end

function library.operation(value)
    local result = value + 100

    return result
end

function library.finish()
    return false
end

return library

One must save the code and observe the results in the running program. In the moment that the new code of the library is re-executed by dofile(), the running code for library.hello() and library.operation() will change. The sum is now performed in increments of 100 by 100 and the message will be Hello, my name (still) is Franco!!!.

Therefore, it is possible to keep the program running and improve it continually, as necessary. To finish the program, it suffices to change the library once again. The only required change is returning true in library.finish().

local library = {}

function library.hello()
    print("Goodbye!")
end

function library.operation(value)
    local result = 0

    return result
end

function library.finish()
    return true
end

return library

When the file is saved, the program will end when the code is re-evaluated by dofile().

To improve the solution, the library would ideally be reloaded only when the library.lua had changed. To do this, one could check the last modification of the file using a file system library. The value of the last modification would be kept and compared to the new one. If the new value was more recent than the older one, it is updated and the library is reloaded.

After introducing non-block input operations, another option would be to define a shortcut key to reload the code (for instance, F5).

Finally, the solution can use require() instead of dofile() with some modifications. To do this, one must change the value for the library in the table package.loaded (documentation). This is necessary because require() optimizes the process of loading libraries: libraries that have been loaded previously when the program was running are not reloaded, to improve the performance. In this case, this is not desirable.

local library = require("library")

-- <https://www.francogarcia.com/en/blog/learn-programming-records/>
function sleep(time_seconds)
    local finish = tonumber(os.clock() + time_seconds);
    while (os.clock() < finish) do
        -- Wait the passage of time, wasting cycles of the processor.
    end
end

local finish = false
local number = 0
while (not finish) do
    package.loaded.library = nil
    library = require("library")
    library.hello()

    number = library.operation(number)
    print(number)

    sleep(0.5)

    finish = library.finish()
    if (finish) then
        print("Preparing to the the program...")
    end
end

print("End of the program")

Although the example uses Lua, it also can be implemented in GDScript, Python and JavaScript.

Hot Swap in GDScript using Godot Engine

Godot provides the hot swap feature predefined; it only has to be enabled:

  1. For projects, one can enable the option to synchronize changes between scenes and scripts (documentation). They are in the Debug menu as Synchronize Scene Changes and Synchronize Script Changes;
  2. For the editor, one can use the tool mode in scripts (documentation).

To use the GDScript version, it suffices to enable synchronization of changes in scripts.

The script will be slightly different, because Godot is an engine, and imposes conventions and presupposes forms of use. For Godot Engine, code that must be repeated over time is defined in special methods: _process() (documentation) and _physics_process() (documentation). Thus, instead of using a repetition structure, one can implement the code that will be repeated in one of the two methods. The code will be repeated every step of the game loop.

extends Node

static func hello():
    print("Hello, my name is Franco!")

static func operation(value):
    var result = value + 1

    return result

static func finish():
    # Change to true and save the file to end the program.
    return false
extends Node

var library = preload("res://library.gd").new()

var finish = false
var number = 0
var accumulated_time = 0.0

# _process() is repeated by the engine on every iteration of the game loop.
func _process(delta):
    accumulated_time += delta
    if (accumulated_time < 0.5):
        return

    accumulated_time = 0.0

    library.hello()

    number = library.operation(number)
    print(number)

    finish = library.finish()
    if (finish):
        print("Preparing to the the program...")
        print("End of the program")
        get_tree().quit()

_process() provides the elapsed time (in seconds) since the last update as a parameter. In the implementation, accumulated_time accumulates each interval of delta to simulate a call of sleep (as busy wait). This allows updating and writing the results at every half a second.

To use the program, it suffices to update the code defined in library.gd with the program running. With the option to synchronize scripts changed enables, the program should reflect the modifications (when possible). In particular, it may be necessary using preload() instead of class_name, because the engine may not allow changing the source code with the project running for class_name.

Hot Swap in Python

In Python, one can use the method reload() from the module importlib (documentation).

def hello():
    print("Hello, my name is Franco!")

def operation(value):
    result = value + 1

    return result

def finish():
    # Change to True and save the file to end the program.
    return False
import library
import time
import importlib

finish = False
number = 0
while (not finish):
    importlib.reload(library)
    library.hello()

    number = library.operation(number)
    print(number)

    time.sleep(0.5)

    finish = library.finish()
    if (finish):
        print("Preparing to the the program...")

print("End of the program")

importlib require recent versions of Python 3. In previous versions, there existed equivalent resources, though they have different names.

Hot Swap in JavaScript

In JavaScript for the browser, the process is slightly more complicated, because it is not possible accessing the system's file system. However, the technique is still possible. As a matter of fact, something similar was explored in the example using React, which uses an implementation of hot swap using a local Web server.

The next sections present simple implementations of how to do it.

Hot Swap in JavaScript with Global Subroutines

For a first example of implementation in JavaScript, one can consider that the library loads global functions. The example would also be possible otherwise (a dictionary could be return in load_script() and one could use the returned value).

function hello() {
    console.log("Hello, my name is Franco!")
}

function operation(value) {
    let result = value + 1

    return result
}

function finish() {
    // Change to true and save the file to end the program.
    return false
}
load_script("./library.js", "library")

async function main() {
    let finished = false
    let number = 0
    while (!finished) {
        hello()

        number = operation(number)
        console.log(number)

        await new Promise(resolve => setTimeout(resolve, 500))

        finished = finish()
        if (finished) {
            console.log("Preparing to the the program...")
        } else {
            load_script("./library.js", "library")
        }
    }

    console.log("End of the program")
}
<!DOCTYPE html>
<html lang="en-US">

  <head>
    <meta charset="utf-8">
    <title>Libraries in JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script>
function load_script(filename, div_id) {
    let parent_element = document.getElementById(div_id)
    console.assert(parent_element)

    let new_script = document.createElement("script")
    new_script.id = filename
    new_script.type = "text/javascript"
    new_script.src = filename + "?timestamp=" + new Date().getTime()
    new_script.setAttribute("async", "")
    parent_element.replaceChildren(new_script)
}
    </script>
  </head>

  <body onload="main()">
    <div id="library"></div>
    <script src="./main.js"></script>
  </body>

</html>

To use the implementation, you must start a local Web server and access an address such as http://localhost:8080/ (assuming port 8080 and an HTML file named index.html).

The important part of the file is written in index.html. In the embedded script defined in <head>, a script is created with the code of the library. In particular, getTime() (documentation) fetches the current time, which value is passed as parameter in the address to avoid cache problems. As the goal is to modify the contents of the file, the browser should not provide the old version. The created script is added into the <div> that stores the code of the library.

The second new resource is the use of onload in <body> (documentation). The implementation defines the code of the main program in a procedure called main(), which is called by onload when the document loads. This ensures that the library script is loaded before its first use.

In main.js, load_script() is at the end of the while loop as a reminder that the modification of the library will happen in the next iteration. The remainder of the implementation is similar to the others. The logic value finish was renamed to finished to avoid name conflicts between the global function end() defined in library.js.

Hot Swap in JavaScript with Modules

For a second implementation, one can consider using modules.

function hello() {
    console.log("Hello, my name is Franco!")
}

function operation(value) {
    let result = value + 1

    return result
}

function finish() {
    // Change to true and save the file to end the program.
    return false
}

export {
    hello,
    operation,
    finish
}
let finish = false
let number = 0
while (!finish) {
    let library = await import("./library.js?" + new Date().getTime())
    library.hello()

    number = library.operation(number)
    console.log(number)

    await new Promise(resolve => setTimeout(resolve, 500))

    finish = library.finish()
    if (finish) {
        console.log("Preparing to the the program...")
    }
}

console.log("End of the program")
<!DOCTYPE html>
<html lang="en-US">

  <head>
    <meta charset="utf-8">
    <title>Libraries in JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body>
    <script src="./main.js" type="module"></script>
  </body>

</html>

The implementations with modules become closer to the ones used in other languages, as it makes possible using import() dynamically. It is simple and concise, though it requires a modern browser (as recent version of Firefox).

New Items for Your Inventory

Tools:

  • Package managers;
  • Content Delivery Network (CDN);
  • Unit test frameworks;
  • Web server;
  • Hot swap.

Skills:

  • Creation of libraries;
  • Use of libraries;
  • Use of package managers;
  • Use of local Web servers.

Concepts:

  • Libraries;
  • Modules;
  • Frameworks;
  • Engines;
  • Dependencies;
  • Namespaces;
  • Software Development Kit (SDK or devkit);
  • Inversion of control;
  • Licenses;
  • Content Delivery Network (CDN);
  • Server content to the Internet, using servers;
  • Adapter (Wrapper or Translator);
  • Programming by Contract;
  • SOLID.

Programming resources:

  • Use and creation of libraries;
  • Callbacks;
  • Interfaces.

Practice

There are three ways of practicing the content of this topic:

  1. Creating your own libraries;
  2. Using the libraries that you have created;
  3. Using external libraries (dependencies).

A good activity in potential is reviewing the previous topics. You can group definitions of subroutines and data types in libraries for JavaScript, Python, Lua and GDScript. This way, you will be able to reuse the acquired knowledge in your future projects.

Furthermore, you can explore some libraries presented in this topic. They were varied, as a way of illustrating new possibilities and expand your repertory.

Next Steps

In programming, no one knows everything. The author has a doctorate in Computer Science, yet he still learns something new every day, even when solving simple problems. The knowledge and experience accumulate; though, in programming, everyone is an eternal apprentice.

It is essential perceiving that there is much to learn. Honesty and self-evaluation are important to keep improving. After all, the identification of troubling areas or weak knowledge allow focusing on improving.

Similarly, it is important acknowledging what one knows. Everyone knows some things, and do not know many others. This is interesting, because it means that the knowledge and abilities of a person can complement those from another. Communities that help themselves have better changes of prospering.

This is what happens in programming. Software development is a collaborative activity, even for solo programmers. Practically every developer uses tools and libraries created by other people. Likewise, every person can help improving existing tools and libraries.

The participation does not require only the creation of new libraries. Actually, it is possible to:

  • Improve existing libraries;
  • Report issues and bugs that have been found during use;
  • Provide suggestions for improvements.

Collaborations such as the previous ones allow to continuously improve processes, tools and programming resources. They are some reasons of the success of open source and free software solutions.

Furthermore, the use of libraries allow benefiting from the knowledge and expertise of other people as a way of overcoming gaps on one's own knowledge. In other words, one can solve problems by knowing how to apply existing tools. It is not always needed to create a new one -- especially if a suitable alternative does already exist.

One should not reinvent the wheels without reasons. It is the opposite: one should use whatever is at one's reach to achieve success faster. Programming languages are tools; libraries are tools; development programs are tools. Therefore, it is worth to use wheel of giants whenever possible, taking suitable care not to fall.

Without libraries, one's programming resources are limited to her/his own knowledge. With libraries, one can benefit from the knowledge and efforts of communities of people that contribute to create better tools and systems. Not only for programming and computing: libraries exist for several areas of knowledge.

Particularly to this topic, the chosen external libraries for each language were different. This was intentional: there are many ways to continue learning and expanding your knowledge. Your current knowledge is composed by fundamentals. You have created the basis; this is the moment to start the foundations of your own projects.

In future topics, the author intends to expand the material with simulations and use of multimedia content. Before, though, it is still worth exploring the command line. This time, you will manipulate arguments passed by command line interpreters for your own programs.

  1. Introduction;
  2. Entry point and program structure;
  3. Output (for console or terminal);
  4. Data types;
  5. Variables and constants;
  6. Input (for console or terminal);
  7. Arithmetic and basic Mathematics;
  8. Relational operations and comparisons;
  9. Logic operations and Boolean Algebra;
  10. Conditional (or selection) structures;
  11. Subroutines: functions and procedures;
  12. Repetition structures (or loops);
  13. Arrays, collections and data structures;
  14. Records (structs);
  15. Files and serialization (marshalling);
  16. Libraries;
  17. Command line input;
  18. Bitwise operations;
  19. Tests and debugging.
  • Informatics
  • Programming
  • Beginner
  • Computational Thinking
  • Learn Programming
  • Python
  • Lua
  • Javascript
  • Godot
  • Gdscript