This document describes how Horse64 code is organized, and how you can integrate third-party libraries and code. For a simple program you can have some single myprogram.h64
text file with all your code, but for non-trivial programs you'll want to use multiple files. Each such file is then called a module, but read on for details:
A package is Horse64's name for a more-or-less complete project, e.g. a project directory holding your piece of software or library would be a package. (Or as another example, core.horse64.org
is the package holding all the standard library functionality, with things like I/O and math basics likely needed by most programs.)
A module is Horse64's name for what you organize in either a single code file, or whatever is then grouped in a directory. For example, the math
module in core.horse64.org
is implemented in a respective math.h64
file. The compiler.main
module sits in a compiler/main.h64
file, and it's a submodule of the compiler
module.
To use code from a separate module, simply import it:
import math from core.horse64.org
func main {
print("Maximum of 5 and 6: " + math.max(5, 6).as_str())
}
An import simply declares what external module you want to use. All global vars and funcs and types in that module then become available by prefixing them with module.
, see math.
in above example.
The from
clause of an import statement declares the package that module is in. The clause can and should be omitted if it's from the same package or project that your code file is already in.
Global initialization code, like assigned default values for global variables, runs for each import module before the actual program main entrypoint starts.
The main entrypoint is the main
function you have to declare somewhere for your program.
Summed up: my/random/dir/codefile.h64
becomes a module that can be imported as my.random.dir.codefile
for use.
Neighbor modules in the same subdirectory also always get pulled in and processed. Example:
In file mymodule/neighbor.h64
:
import mytype
extend mytype.Storage {
var buckets
}
In file mymodule/mytype.h64
:
type Storage {
var brooms
}
When you use import mymodule.mytype
, then the custom type mymodule.mytype.Storage
will automatically have both the var attributes brooms
and buckets
, even if you never explicitly import mymodule.neighbor
anywhere.
This is because any import of a module also processes all the ones in the same directory, allowing easy use of extending types like in the example above. However, anything in deeper subdirectories or outside of the directory won't be pulled in.
As a special case, when a code file has the same name as the surrounding directory e.g. named mymodule
, it sort of "represents" the functions of that module directory.
See here, an example:
In file mymodule/mymodule.h64
:
var test = print_hello()
func print_hello {
print("Hello from mymodule")
}
Above code gets triggered with this import:
import mymodule
Also, after this import, mymodule.print_hello
and mymodule.test
can be accessed.
To download packages for your project, use horp in your project directory:
$ horp install my.package.name
Read more in horp's documentation to find out about details.