ChucK : Language Specification > Import System

version: 1.5.x.x (numchucks)

Import System

The @import system imports classes and operator overloads from other ChucK (.ck) and chugin (.chug) files; it is designed to facilitate creating, using, and distributing multi-file programs and libraries.

This feature was introduced in chuck-1.5.4.0 and thus requires that version or higher.

View sample code for importing files

syntax

@import statements can appear anywhere in .ck file, so long as they are on the top-most scope. Some example usage of @import:

    // import a ChucK file (.ck)
    @import "Foo.ck"

    // import a chugin (.chug or .chug.wasm)
    @import "Bar.chug"

    // multiple imports can be grouped together
    @import { "AAA.ck", "BBB.chug", "CCC" }

    // can use implicit filename (with no extension)
    // if the file 'Blarg' exists, it will be imported as a .ck file
    // if not, @import will automatically match for .ck and .chug files
    @import "Blarg"

    // import a file using absolute path
    @import "/Users/ge/chuck/examples/import/foo-user.ck"

    // paths are relative to the location of the container .ck file
    @import "../dir/Blarg.ck"

what gets imported?

The @import system imports all public—and only public—class definitions and operator overloads from imported ChucK files. For example:

in Foo.ck
// define a public Foo class (picked up by @import)
public class Foo
{
    // member variable
    int num;

    // constructor
    fun Foo( int n ) { n => num; }
}

// a non-public class definition (ignored by @import)
class Bar
{
    // ...
}

// public operator overloading + for Foo (picked up by @import)
public Foo @operator +( Foo lhs, Foo rhs )
{
    // return a new Foo
    return new Foo( lhs.num + rhs.num );
}

// non-public binary operator overload for '=>' (ignored by @import)
fun void @operator =^( Foo lhs, Foo rhs )
{ 
    // for sake of example, just print contents
    <<< lhs.num, "=^", rhs.num >>>;
}
in test.ck
// import Foo.ck
@import "Foo.ck"

// instantiate class defined in imported file
Foo a(1), b(2);

// use operator overload defined in imported file
<<< (a + b).num >>>;
Imports works recursively; it is possible to @import files that @import other files, etc. There is built-in cycle-detection; if an @import cycle is detected, a compiler error will be issued.

Imports are resolved at compile-time, before the rest of the host file is evaluated; this makes it possible for a a host .ck file to @import one or more files and make use of their contents immediately (when it comes to dependency management, this compile-time import mechanism is more powerful than the run-time Machine.add()).

If the imported file is a chugin (.chug) file, @import will resolve the filename (see next section for details) and load the chugin. Unlike .ck files, all contents that a chugin adds to the ChucK type system will be imported.

filename resolution

It is possible to import a filename without an extension. As an example, if a host .ck file contains this statement:
    @import "Blarg"
In this case, the @import system will attempt to automatically locate and resolve "Blarg"—to either a .ck file or a .chug file, while taking import paths into account. More specifically, the import system will attempt to locate on disk (starting from the directory where the host .ck file resides) and match for different extensions—e.g., "Blarg", "Blarg.ck", "Blarg.chug", and "Blarg.chug.wasm" (for WebChucK)—resolving "Blarg" to the first matching file. If no match is found, the import system will expand the search to platform-specific search paths.

public classes

As of ChucK 1.5.4.0, the restriction of one-public-class-per-file is lifted, making it possible to define multiple public classes in the same .ck file.

While non-public classes are not imported, they can used from public classes in the same file. For example:

in Bar.ck
// non-public class used by Bar
class Blarg
{
    // member
    int num;
    // constructor
    fun Blarg( int n ) { n => num; }
}

// public class
public class Bar
{
    // member
    Blarg blarg(5);
}
As a somewhat more practical example, ks-chord.ck (a four-voice Karplus Strong chord filter) defines a public KSChord class, which in turns makes use of a non-public KS class. A host file that @import "ks-chord.ck" would be able to instantiate KSChord objects—but not KS objects.

CAVEATS: public classes cannot access local variables (i.e., file-scope variables defined outside of class definitions). In general, non-public classes can access local variable—as long as the containing file is not being imported. This restriction is necessary because imported files have no file-level environment of their own. For example:

in Bar-Error.ck
// a local variable (to the file, defined outside of classes)
int theVar;

// non-public class used by Bar
class Blarg
{
    // ERROR: if this file is imported, will result in compiler error
    // (due to local variable `theVar` not being recognized)
    theVar + 1 => int num;
}

// public class
public class Bar
{
    // member
    Blarg blarg;
}

additional usage notes

@import only adds new definitions to the type system, and will never automatically run any code; client code must first instantiate class or use an operator to execute the contents of imported files.

This makes it possible to write unit-test code for public classes in the same file in which it is defined; any code outside of public class/operator definition will NOT be type-checked or run (this code will only scanned for syntax):

Bar.ck
// a class definition (public; will be picked by @import)
public class Bar extends Chugraph
{
    // ...
}

// unit-test or provide example for KS (will be ignored by @import)
Bar bar => dac;
// ...
< (prev): manipulating time | (up): language specification | (next): concurrency >