The Basics
Encore uses active classes which are classes with asynchronous method call semantics in order to achieve parallelism-by-default. This section gives a concise overview of encore’s classes. The Section “Encore Syntax” and others will describe the language in much greater detail.
Hello, World!
The obligatory hello-world program, hello.enc
, looks like this:
active class Main
def main() : unit
println("Hello, World!")
end
end
To compile and run the program just run encorec --run hello.enc
.
$ encorec --run hello.enc
Hello, World!
It is important to know that every legal encore program needs to have an active class called Main, with a method called main. The runtime will allocate one object of class Main and begin execution in its main method.
Compiler Options
Running encorec foo.enc
will typecheck the source and produce the executable foo
. The following options are supported:
--import [dirs] | -I [dirs] colon separated list of directories in which to look for modules.
--out-file [file] | -o [file] Specify output file.
--custom-flags [flags] | -F [flags] Supply custom flags for the C compiler. (Use quotation marks to supply more than one flag)
--generate-c | -c Outputs intermediate C fields in separate source tree.
--debug | -g Inserts debugging symbols in executable. Use with -c for improved debugging experience.
--type-check | -tc Only type check program, do not produce an executable.
--pretty-print | -pp Pretty print the AST immediately after parsing.
--literate | Literate programming mode. Code blocks are delimited by '#+begin_src' and '#+end_src'.
--verbose | -v Print debug information during compiler stages.
--optimize N | -O N Optimise produced executable. N=0,1,2 or 3.
--profile | -pg Embed profiling information in the executable.
--run | Compile and run the program, but do not produce executable file.
--no-gc | DEBUG: disable GC and use C-malloc for allocation.
--help | Display this information.
For instance, keep the intermediate C-files, invoke the following: encorec -c foo.enc
.
Active/Passive Classes
Encore supports active and passive classes. Allocating an object from those classes gives active or passive objects, respectively. To make a class active, the active
keyword is necessary. To make a class passive, use modifier class P
,
where modifier
is linear
, local
, read
, or subord
.
Calling a method on an active object will have that method execute concurrently (also in parallel, if enough cores are available). The return type of an active method is a future (see section “Futures”) and not the return type in the method declaration.
In contrast to active classes, methods of passive classes execute synchronously, in the calling thread. Passive objects are similar to what you’d expect in a Java-like language (but without
any synchronisation overhead).
Note that all fields in an active class are private but all fields are public in a passive class.
A class may have a method called init which is used as the constructor. It is called when appending arguments to object construction (new Foo(42)
) and cannot be called on its own.
Example: Active Class
The follow example considers an active class Foo
. A Foo
instance has an ID string, and
calling the printID
method will print that string 5 times to stdout. The main
method creates two Foo
instances, and has them print their IDs:
active class Main
def main() : unit
val obj1 = new Foo
val obj2 = new Foo
obj1.setID("obj1")
obj2.setID("obj2")
obj1.printID()
obj2.printID()
end
end
local class Foo
var id : String
def setID(new_id : String) : unit
this.id = new_id
end
def printID() : unit
var i = 0
while i < 5 do
i = i + 1
println(this.id)
end
end
end
Executing this program gives nondeterministic output:
$ encorec -run ex_active.enc
obj1
obj1
obj1
obj1
obj1
obj2
obj2
obj2
obj2
obj2
The behavior of this program can be made deterministic using get
applied to
each call to printID
. This call waits for the method invocation to finish
before proceeding. See the section “Futures” for more information.
Example: Passive Class
Passive classes are used the way regular classes in Java are used, namely, to build
data structures. Unlike Java, they are unsynchronised, which means that an Encore
programmer needs to work harder to ensure passive classes are used safely. The mechanism
for achieving safety on passive classes is the Kappa capability system, which consists
of a small number of annotations to describe how objects of passive classes are shared.
The following class, duplicated from the previous example, includes the annotation
local
to state that instances of this class cannot be passed between actors.
local class Foo
var id : String
def setID(new_id : String) : unit
this.id = new_id
end
def printID() : unit
var i = 0
while i < 5 do
i = i + 1
println(this.id)
end
end
end
Other capabilities include read
for read-only objects, linear
for objects that have
only a single reference to them, and subord
for objects that are cannot be referred
to outside of another passive object.