Skip to content

Modules

A Hylo program is composed of modules, each of which is composed of one or more files. A module that defines a public main function is called an entry module, of which there must be exactly one per program.

The Hello, World program we wrote earlier is made of two modules. The first contains the hello.hylo file and is the program’s entry module. The second is Hylo’s standard library, which is always implicitly imported, and defines commonly-used components like the Int and String types, and the print function used in hello.hylo.

Each module defines an API resilience boundary: only public declarations are visible outside the module, and changes to non-public declarations, or to the bodies of public functions in the module cannot cause code outside the module to fail compilation. A module may also define an ABI resilience boundary, within which code and details such as type layout are never encoded into other compiled modules (e.g. via inlining).

You can bundle multiple files in a single module by passing all of them as arguments to hc. For example, in a separate file we can define a function, that prints a specialized greeting, and call it from Hello.hylo:

// In `Hello.hylo`
public fun main() {
greet("World")
}
// In `Greet.hylo`
module fun greet(name: String) {
print("Hello, ${name}!")
}
// Note: expected to fail compilation until strings are implemented.

Here, we declare a function greet that takes a single argument of type String. The underscore (i.e., _) before the parameter name means that arguments passed here must be unlabeled. We’ll come back to argument labels later.

To run this program:

  • Run the command hc Hello.hylo Greet.hylo -o hello
  • Run the command ./hello

Alternatively, you can put both source files in a subdirectory, say Sources/, and compile the program with hc Sources -o hello.

Note that greet must be declared as internal to be visible from another file in the same module. By default, all declarations are fileprivate. Use public to make a declaration visible outside of the module.

To expose a non-public function for inlining in other modules, you can use the @exposed annotation. The function’s intermediate representation (IR) will be included in the module bundle, but we still can’t call it directly from other modules.

@exposed
fun double(x: Int32) -> Int32 {
return x + 2
}

The simplest way to work with multiple modules is to gather source files into different subdirectories. For example, let’s move greet in a different module, using the following directory tree:

  • DirectorySources
    • DirectoryHello
      • Hello.hylo
    • DirectoryGreetings
      • Greetings.hylo

Let’s also slightly modify both source files:

Sources/Hello/Hello.hylo
import Greetings
public fun main() {
greet("World")
}
Sources/Greetings/Greetings.hylo
public fun greet(name: String) {
print("Hello, ${name}!")
}

The statement import Greetings at the top of Hello.hylo tells the compiler it should import the module Greetings when it compiles that source file. Implicitly, that makes Greetings a dependency of Hello.

Notice that greet had to be made public so it could be visible to other modules. As such, it can be called from Hello.hylo.

To build this program, you can set up CMake as your build system or contribute integration for another one. In the future, we are planning to add a dedicated build system and package manager for Hylo.