You are on page 1of 110

Structure

Overview
Fantom software is structured using three primary abstractions:
• Pods: modules of deployment and versioning
• Types: basic units of object oriented type system
• Slots: fields and methods
These abstractions are organized into a three level namespace which uses the following syntax for
qualified names:
• pod
• pod::Type
• pod::Type.slot

Pods
Pods are the top of Fantom's namespace as well as the unit of deployment. A pod's name is
globally unique and is used to organize the top level of Fantom's namespace. Pod names are
similar to Java packages or C# namespaces. To guarantee uniqueness, try use a naming
convention which won't produce conflicts (see conventions).
Pods are also the fundamental unit of deployment, versioning, and dependency management. In
this role pods are like Java JAR files or .NET DLLs. A pod is a standard zip file which bundles the
Fantom code for the pod's types, metadata, plus any associated file resources.
The sys::Pod type is the reflection API for working with pods installed in a given Fantom
installation. Code examples for common pod operations:
Pod.list // list the pods installed
Pod.find("acmeFoo") // find a pod (throws exception if not found)
Pod.find("acmeFoo", false) // find a pod (returns null if not found)
myPod.file(`/img/icon.png`) // lookup a resource file in myPod
`fan://myPod/img/icon.png`.get // lookup a resource file in myPod
Pod.of(someObj) // get the pod of an object

See Pods chapter for more details.

Types
A Type is an object oriented class which encapsulates state and behavior. Types are contained
within pods and identified by a name unique within that pod. The :: double colon is used to
combine the pod name with the type name to create the qualified name or qname. Because pod
names are globally unique, a type's qname is also globally unique. For example sys::Str is the
qname of the Str type which is contained by the sys pod.
There are two variations of Types in Fantom: classes and mixins.
The sys::Type type is the reflection API for working with types at runtime. Code snippets for
common type operations:
Type.of(someObj) // get the type of the an object
myPod.types // list the types in myPod
myPod.findType("Foo") // find a type within myPod by its simple name
Type.find("myPod::Foo") // lookup a type by its qualified name
Int# // type literal for sys::Int
someType.fits(Num#) // reflective version of is/instanceof operator
Slots
Types encapsulate state and behavior as a collection of slots. Slots are named uniquely within a
given type. The . dot is used to combine the parent type's qname to create the slot's qname. For
example sys::DateTime.now is the globally unique qualified name which identifies the now method
within the DateTime type within the sys pod.
There are two types of slots:
• Methods: model behavior
• Fields: model state
The sys::Slot type is the reflection API for working with slots at runtime. Code examples for
commonly used slot operations:
someType.slot("xyz") // lookup the slot called xyz on someType
Slot.find("myPod::Foo.xyz") // looukp a slot by its qualified name
method.call([arg0, arg1]) // invoke method using reflection
method.func // the function which implements the method
field.get(instance) // get a field using reflection
SomeType#xyx // slot literal for slot on SomeType
#xyx // slot literal current type

All slots are keyed by a unique name. This means Fantom does not support methods overridden
by parameter type like Java or C#. Although you may find this to be a drag on occasion, there are a
couple features in Fan that make this restriction quite palatable. First method parameters may
have defaults - this eliminates the convenience methods commonly used in Java or C# APIs.
Second all types subclass from Obj - this eliminates the API bloat required to support all the
primitives in an API like java.io.PrintWriter. Lastly, constructors in Fantom are named which
eliminates another common requirement for parameter based overloading. The benefit of this
restriction is the really cool ability to lookup methods simply by name or qname making reflective
programming and dynamic invocation a zillion times simpler.

Methods
Methods are the basic unit for encapsulating behavior in Fantom. Methods are really just slot
wrappers for a function. Every method has a return type and zero or more typed parameters.
Methods which don't return an object have a return type of sys::Void.
The sys::Method API is used to work with methods reflectively at runtime.
Methods are discussed in depth in the Methods chapter.

Fields
Fields are used to model state in a given type. Fields in Fantom are composed of three concepts:
• Getter: a method used to access the current value of the field;
• Setter: a method used to change the current value of the field;
• Storage: a storage location in memory for the current value;
Most fields have all three components, but typically the getter and setter is auto-generated by the
compiler. Const fields have only storage and no getter or setter. Fantom also allows abstract and
calculated fields which have a getter and setter, but no storage.
The sys::Field API is used to work with fields reflectively at runtime.
We'll take a deep dive into fields later in the Fields chapter.
Literals
Overview
The following types have a literal syntax:
• sys::Bool
• sys::Int
• sys::Float
• sys::Decimal
• sys::Str
• sys::Duration
• sys::Uri
• sys::Type
• sys::Slot
• sys::Range
• sys::List
• sys::Map
The three types Bool, Int, and Float are value-types. These types are not necessarily passed as
object references, but rather passed by value on the call stack. When value types are coerced
to/from reference types like Obj and Num, the compiler will generate boxing/unboxing operations.

Bool
There are exactly two values of sys::Bool which are represented using
the true and falsekeywords.
As a value-type Bool fields default to false instead of null. However Bool? does default to null.

Int
sys::Int is used to represent a 64-bit signed integer. Fantom does not have any integer types for
smaller precisions. Fantom also uses Int to represent a single character of a string as a Unicode
code point (which happens to be handy because there are actually more than 2^16 Unicode
characters). Int is const which means that all instances are immutable.
Int literals are expressed as a string of decimal digits. An Int can also be represented in
hexadecimal if prefixed with 0x. Octal notation is not supported. You can use the _ underbar
anywhere within an Int literal as a separator to make your code more readable.
Fantom also permits C style character literals to represent a Unicode code point as an Int literal.
Character literals are surrounded with the tick and support the following escapes:
\b \f \n \r \t \" \' \` \$ \\ \uXXXX

The last escape \uXXXX specifies a Unicode code point using a a four digit hexadecimal number.
Int literal examples:
45
-89_039
0xcafebabe
0xCAFE_BABE
'?'
'\n'
'\u03ab'
'\u00F2'
As a value-type Int fields default to 0 instead of null. However Int? does default to null.

Float
sys::Float is used to represent a 64-bit floating point number. Fantom does not have a type for
32-bit floats. Float is const which means that instances are immutable.
Float literals are expressed like C, Java, etc using a string of decimal digits, optional dot and
fraction, and optional exponent. A "f" or "F" suffix is required on Floats to distinguish from
Decimals. You can use the _ underbar as a separator. Examples of Float literals:
3.0f
3f
3.0F
123_456.0f
3e6f
0.2e+6f
1_2.3_7e-5_6f

As a value-type Float fields default to 0.0f instead of null. However Float? does default to null.

Decimal
sys::Decimal is used to immutably represent a decimal floating point which provides better
precision than a Float. Decimals are ideal for financial applications where Floats may incur
rounding errors. Decimals are backed by BigDecimal in Java and System.Decimal in .NET.
Decimal literals are expressed just like Float literals except they use the "d" or "D" suffix. If a float
literal with a fraction or exponent is expressed with no suffix, then it is assumed to be a decimal.
Examples of Decimal literals:
4d
4.0
4.00
123_456d
3e6
0.2e+6D
1_2.3_7e-5_6

NOTE: decimals don't operate exactly the same between the Java and .NET platform. Java uses
BigDecimal which has an infinite range, while .NET uses System.Decimal with a range of of 28
significant digits and a range of 1E-28 to 7.9E+28. There is also a difference in equality between
the two platforms:
3.00 == 3.0 => false on Java
3.00 == 3.0 => true on .NET
3.00 <=> 3.0 => zero on both platforms

Java treats trailing zeros as significant for equality, but they are insignificant on .NET. However
both platforms produce consistent results for the Obj.compare method.

Str
sys::Str is used to represent a sequence of Unicode characters. Str is const which means all
instances of Str are immutable. Use sys::StrBuf when you need a mutable sequence of
characters.
Str literals are surrounded by the " double quote character. Special characters may be escaped
using the list of escape sequences specified above for Int character literals. A couple Str literal
examples:
"hello"
"line 1\nline 2"
"It is 73\u00B0 Fahrenheit outside!"

Multi-line Strs
Str literals may span multiple lines in which case the newlines are always normalized
to \nregardless of how newlines were encoded in the source code text. The first non-whitespace
char of each line must be aligned to the right of the opening quote or else it is a compile time error:
x :=
"line 1
line 2
line3"

The example above compiles into "line1\n line2\nline3". Note that spacing to the right of the
quote is maintained, but spacing to the left is stripped off in the string literal. If you use tabs then
you must use a matching number of leading tabs followed by space characters:
\t\tx := "line 1
\t\t line 2"

Str Interpolation
Str literals support string interpolation which allow arbitrary Fantom expressions to be embedded
inside the string literals. Embedded expressions are prefixed using the $ dollar sign and
surrounded with { and } braces. If the expression is a simple identifier or sequence of dotted
identifiers then the braces may be omitted. Use the \$ escape sequence if you wish to express the
dollar sign itself.
Interpolated strings are expressions which compute a new Str at runtime - they are merely syntax
sugar for string concatenation. For example:
"x is $x, in hex $x.toHex, and x+8 is ${x+8}"

is syntax sugar for:


"x is " + x + ", in hex " + x.toHex + ", and x+8 is " + (x+8)

String interpolation makes string formatting easier to read and write. Fantom coding convention is
to always use string interpolation rather than concatenation.

Locale Literals
Str interpolation supports a special syntax to easily work with localized strings:
// qualified pod::key
"$<pod::key>" => Pod.find("pod").locale("key")

// lookup key within current pod


"$<key>" => EnclosingType#.pod.locale("key")

// lookup key and automatically add key to `locale/en.props`


"$<key=Text>" => EnclosingType#.pod.locale("key", "Text")

Refer to Localization for in in-depth discussion.

Triple Quotes
Fantom also supports """ triple quoted string literals. These work exactly like normal string literals
except that you don't need to escape the double quote " character. Interpolation and multi-line
work exactly the same:
echo("""Do you know "What lies beneath the shadow of the statue"?""")
Str DSL
You can also write a Str literal using the DSL syntax. A Str DSL can contain any character except
the sequence "|>". Neither the "\" or "$" character are treated specially:
echo(Str <|no \ or $ escapes need, and
multi-line works too|>)

Str DSL literals may be multi-line following the leading whitespace rules for standard strings.

Duration
In Java, an API which requires a measurement of time typically uses a long with the number of
milliseconds. This tends to be a bit ambiguous and becomes problematic when you need finer
precision. Fantom APIs always use a typed value for time. Absolute time measurement is
represented using sys::DateTime and relative time measurement is represented by sys::Duration-
both are normalized using nanosecond precision. For example to represent 5 seconds you could
use the Duration.make constructor:
Duration.make(5_000_000_000) // longhand
Duration(5_000_000_000) // shorthand

But all those zeros make it unwieldy. Plus it is a little inefficient because it requires creating a new
instance of Duration every time the expression is executed. In Fantom, Durations are expressed
using a literal syntax formatted as a decimal number with an optional dotted fraction and one of the
following suffixes:
ns: nanoseconds (x 1)
ms: milliseconds (x 1,000,000)
sec: seconds (x 1,000,000,000)
min: minutes (x 60,000,000,000)
hr: hours (x 3,600,000,000,000)
day: days (x 86,400,000,000,000)

Examples of Duration literals:


4ns
100ms
-0.5hr

Uri
The sys::Uri class is used to represent a Uniform Resource Identifier which is the foundation of
Fantom's subsystem for naming and resolution. Uris have their own literal syntax using the back
tick:
`index.html`
`/some/path/file.txt`
`http://fantom.org/`
`TPS Report.doc`

Note that when working with URIs in Fantom and representing them as literals we always
usestandard form. For example a space is represented using a normal space, not encoded as
"%20":
`TPS Report.doc`.toStr // yields "TPS Report.doc"
`TPS Report.doc`.encode // yields "TPS%20Report.doc"
`TPS%20Report.doc`.toStr // yields "TPS%20Report.doc" (probably not what you want)
`TPS%20Report.doc`.encode // yields "TPS%2520Report.doc" (probably not what you want)

Like strings, you can embed the standard escape sequences into a Uri literal including Unicode
code points. Unicode chars are UTF-8 encoded into octects before the URI is percent encoded
according to RFC 3986 (see sys::Uri.encode).
Uris support interpolation following the same rules as Str interpolation:
file := "file.txt"
`/dir/$file` => ("/dir/" + file).toUri

Type
The sys::Type class is the foundation of the Fantom reflection APIs. Typically Type instances are
queried using the sys::Type.of method. But you can also represent a Type instance using the type
literal syntax which is simply a type name followed by the # symbol:
Str#
acme::SomeType#

If a fully qualified type name is not specified, then the typename is resolved according to the
source file's using statements.

Slot
You can create a slot literal using the syntax:
Int#plus
#echo

If the type name is omitted, then the slot literal is resolved against the enclosing class. A slot literal
resolves to a sys::Field or sys::Method. Slot literals have the same semantics as reflection
via Type.slot except they can be statically checked by the compiler.

Range
A sys::Range represents a contiguous range of integers from start to end. Ranges may be
represented as literals in Fantom source code as start..end for an inclusive end or start..<endfor
an exclusive range. Inclusive and exclusive determines if the end index is included in the range
(start is always inclusive). Example of Range literals:
0..5 // 0 to 5 (end is inclusive)
0..<5 // 0 to 4 (end is exclusive)
x..<y // x to y-1 (end is exclusive)

Note that the .. and ..< operators may be used with any arbitrary expression according tooperator
precedence. These operators are just syntax sugar for constructing a range viasys::Range.make.

List
The sys::List class stores an ordered list of objects. Lists may be instantiated using the following
literal syntax:
// syntax format where V is the optional item type, and
// the items are arbitrary expressions:
V[item0, item1, ... itemN]

// examples
Int[10, 20, 30] // list of the three Ints 10, 20, and 30
[10, 20, 30] // same as above using type inference
Int[,] // empty list of Ints
[,] // empty list of Obj?

In most simple cases a List literal is just a list of comma separated expressions inside square
brackets. If the type prefix is omitted, then type inference is used to determine the type of the
items. The type of the items is determined by computing the most specific class all the items share
(mixins types are not taken into account). For example:
[1, 2, 3] // evaluates to Int[]
[1, null, 3] // evaluates to Int?[]
[1f, 2f, 3f] // evaluates to Float[]
[1, 2f, 3] // evaluates to Num[]
[1, "2", 3] // evaluates to Obj[]
Num[1, 2, 3] // evaluates to Num[]
[[10,20], [30]] // evaluates to Int[][]

In the case of [1,2f,3] the list contains both Ints and Floats which share Num as their most
specific common base class. However the list [1,"2",3] contains Ints and Strs which don't share
a common base class other than Obj. The list Num[1,2,3] would evaluate to Int[] if type inference
was used, but if we might put Floats into the list, then we need to explicitly specify the type.
Often the compiler will infer a list to have a non-nullable type. If the list might store null values, then
you will need to explicitly type it:
[1,2,3] // cannot store null
Int?[1,2,3] // can store null

The empty list is denoted using the special syntax [,]. Often you will specify a type - for
exampleStr[,] is an empty list of strings. If a type is not specified, then the empty list evaluates to
aObj?[,].
If a list literal without an explicit type is used as a field initializer, then it infers its type from the
field's declared type:
Str[] names := [,] // initial value inferred to be Str[,]
Num[] nums := Int[,] // initial value is Int[,]

See Appendix for the formal rules used for type inference of lists.

Map
The sys::Map class stores a set of key/value pairs using a hash table. Maps may be instantiated
using the following literal syntax:
// syntax format where K:V is the optional map type,
// and the keys and values are arbitrary expressions:
[V:K][key0:value0, key1:value1, ... keyN:valueN]

// examples
[Int:Str][1:"one", 2:"two"] // map of Strs keyed by Int
Int:Str[1:"one", 2:"two"] // same as above with shorthand type syntax
[1:"one", 2:"two"] // same as above using type inference
Int:Str[:] // empty Int:Str map
[:] // empty map of Obj:Obj?

The Map literal syntax is like List except we specify the key value pairs using a colon. The type
prefix of a map literal is any valid map signature. If the type prefix is omitted, then type inference is
used to determine the type of the keys and values using the same rules as list literals. For
example:
[1:"one", 2:"two"] // evaluates to Int:Str
[1:"one", 2:null] // evaluates to Int:Str?
[1:"one", 2f:"two"] // evaluates to Num:Str
[1:"one", 2f:null] // evaluates to Num:Str?
[1:"one", 2f:0xabcd] // evaluates to Num:Obj
[0:["one"]] // evaluates to Int:Str[]

The empty map is denoted using the special syntax [:] with or without a type prefix.
Note that maps may not be typed with a nullable key. If you are using type inference, you might
need to explicitly type a map which will store null:
[1:"one", 2:"two"] // cannot store null values
Int:Str?[1:"one", 2:"two"] // can store null values
The type Int:Str? is a map with Int keys and Str? values. However the type [Int:Str]? is map
ofInt:Str where the map variable itself might be null.
If a map literal without an explicit type is used as a field initializer, then it infers its type from the
field's declared type:
Str:File[] files := [:] // initial value inferred as Str:File[:]

See Appendix for the formal rules used for type inference of maps.
Expressions
Operator Precedence
Fantom's expression syntax is very similar to C, Java, C# and company. Operators in order of
precedence:
• Primary: (x) x.y x.y() x->y() x?.y x?.y() x?->y() x[y]
• Unary: ++x --x x++ x-- !x +x -x (T)x &x
• Multiplicative: * / %
• Additive: + -
• Range: .. ..<
• Relational: < <= >= > <=> is isnot as
• Equality: == != === !==
• Conditional And: &&
• Conditional Or: ||
• If Expr: x?t:f x?:y
• Assignment: = *= /= %= += -=
• Collection Add: ,

Shortcut Operators
Fantom is a pure OO language in that everything is an object you can call methods on - even
value-types such as Bool and Int. As such almost all the operators are really just method calls. We
call these operators the shortcut operators because they are just syntax sugar for calling a specific
method:
a + b => a.plus(b)
a - b => a.minus(b)
a * b => a.mult(b)
a / b => a.div(b)
a % b => a.mod(b)
a[b] => a.get(b)
a[b] = c => a.set(b, c)
-a => a.negate()
++a, a++ => a = a.increment()
--a, a-- => a = a.decrement()
a == b => a.equals(b)
a != b => !a.equals(b)
a <=> b => a.compare(b)
a > b => a.compare(b) > 0
a >= b => a.compare(b) >= 0
a < b => a.compare(b) < 0
a <= b => a.compare(b) <= 0
a,b,c => it.add(a).add(b).add(c)

For example say we have two variables a and b both of type Int. Then the expression a+b is really
just syntax sugar for calling sys::Int.plus as a.plus(b). See Method Operators for a detailed
discussion.

Prefix and Postfix Operators


The ++ and -- operators can be prefix or postfix just like C family languages. Both of these
operators assign the result of the call to increment or decrement to the operand variable. If prefix
then the expression evaluates to the assignment. If postfix then the expression evaluates to the
value of the operand before increment or decrement is assigned.
Equality Operators
The equality operators == and != both make use of the sys::Obj.equals virtual method. Most types
override this method to compare value equality. If equals is not overridden, then the default
behavior is to compare reference equality.

Relational Operators
The relational operators like < and > all use the sys::Obj.compare virtual method. Many types with
the notation of ordering will override this method to return -1, 0, or 1. If compare is not overridden,
then the default implementation will compare the result of the operands toStr method.
The compiler translates the numeric return into a boolean condition based on which operator was
used. The special <=> operator returns the Int value of -1, 0, 1 directly. You will commonly use
the <=> operator for custom sorts with a closure:
people.sort |Person a, Person b->Int| { return a.age <=> b.age }

If that code doesn't make any sense to you, then don't worry - just keep reading until we
coverclosures.

Comparisons with Null


The equality and relational operators have special handling if either operand is null such that
aNullErr exception is never raised. For equality a non-null and null are never equal, but two nulls
are always equal. For relational operators, null is always less than a non-null object. Special
handling for null does not apply if the equals or compare method is used as a normal method call.
Nor does this special handling apply for other shortcut operators.

Same Operators
The === and !== operators are called the same and not same operators. These operators are used
to check if two variables reference the same object instance in memory. Unlike the == and!
= shortcut operators, the same and not same operators do not result in the equals method call.
These operators are not allowed to be used against value-types.

Conditional Operators
The conditional !, &&, and || operators are used with boolean expressions. Use && to perform a
logical and and || to perform a logical or. Both of these operators are short circuiting in that the
second test is skipped if the first test is conclusive ('false' for && and true for ||). The ! operator
performs a logical not. Code examples for the conditional operators:
t := true
f := false
t && f => evaluates to false
t && t => evaluates to true
f || t => evaluates to true
!t => evaluates to false

Ternary Operator
The ternary operator combines three expressions as a convenient way to assign a value based on
an if/else condition:
condExpr ? trueExpr : falseExpr

The condExpr must evaluate to a boolean. If condExpr evaluates to true then the whole expression
evaluates to trueExpr, otherwise to falseExpr. Examples:
3 > 4 ? "yes" : "no" => evaluates to "no"
6 > 4 ? "yes" : "no" => evaluates to "yes"

Fantom also supports use of a throw statement as one of the results of a ternary operation:
val := isValid(key) ? map[key] : throw ArgErr("invalid key")

Null Convenience Operators


Fantom supports several of the operators found in Groovy to make working with null more
convenient:
• Elvis Operator x ?: y (look at it sideways as a "smiley" face)
• Safe Invoke x?.y
• Safe Dynamic Invoke x?->y

Elvis Operator
The elvis operator evaluates the left hand side. If it is non-null then it is result of the whole
expression. If it is null, then the result of the whole expression is the right hand side expression.
The right hand side expression is short circuited if the left hand side evaluates to non-null. It is
similar to how you might use the ternary operator:
// hard way
file != null ? file : defaultFile

// easy way
file ?: defaultFile

The elvis operator may not be used on a non-nullable type since by definition it will not be null.
Like the ternary operator the elvis operator may use a throw statement as the right hand side of the
expression:
val := map[key] ?: throw ArgErr("key not found")

Safe Invoke
The safe invoke operators are designed to short circuit if the target of method call or field access is
null. If short circuited, then the whole expression evaluates to null. It is quite useful to skip checking
a bunch of values for null during a call chain:
// hard way
Str? email := null
if (userList != null)
{
user := userList.findUser("bob")
if (user != null) email = user.email
}

// easy way
email := userList?.findUser("bob")?.email

If at any point in a null-safe call chain we detect null, then the whole expression is short circuited
and the expression evaluates to null. You can use ?-> as a null-safe version of the dynamic invoke
operator.
The safe invoke operator may not be used on a non-nullable type since by definition it will not be
null. The result of a safe invoke is always nullable:
x := str.size => x is typed as Int
x := str?.size => x is typed as Int?
Type Checking
The cast operator is used to perform a type conversion. The cast syntax uses parenthesis like C
languages - such as (Int)x. If a type cast fails at runtime, then a sys::CastErr exception is raised.
The is, isnot, and as operators are used check an object's type at runtime:
• operator returns a Bool if the operand implements the specified type (like
is
Java'sinstanceof operator). If target is null, then evaluates to false.
• isnot operator is semantically equivalent to !(x is Type). If target is null then evaluates to
true.
• The as operator returns the object cast to the specified type or null it not an instance of that
type (like C#):
Obj obj := 123
obj is Str => evaluates to false
obj is Num => evaluates to true
obj isnot Str => evaluates to true
obj isnot Num => evaluates to false
obj as Float => evaluates to null
obj as Int => evaluates to 6 (expr is typed as Int)

Nullability of types is not considered when using the is, isnot, and as operators. For example
these two expressions are considered equivalent:
obj is Str
obj is Str?

The as operator by definition returns a nullable type. For example the following expression
evaluates to Str?, not Str:
x := obj as Str => x is typed as Str?

Indexing
Depending on how it is used, the [] operator maps to three different shortcuts:
a[b] => a.get(b)
a[b] = c => a.set(b, c)
a[b] => a.getRange(b) if b is Range

Typically a[b] is a shortcut for calling a.get(b). For example the sys::List.get method allows you
to lookup a list item by it's Int index. Whenever a class supports a get operator method you can
use [] as a shortcut. Consider this code:
list := ["a", "b", "c"]
list.get(2)
list[2]
list.get("2") // error
list["2"] // error

The expression list[2] is exactly the same code as list.get(2). The last two lines result in a
compiler error because we are attempting to pass a Str when an Int is expected.
When the indexing shortcut is used on the left hand side of an assignment such as a[b] = c then
the index operator maps to a.set(b, c). For example these two lines of code have identical
behavior:
map.set("tmj", "Too Much Joy")
map["tmj"] = "Too Much Joy"
If the [] operator is used with a sys::Range index, then we map to the a.getRange(b) method
which performs a slice. Slicing is used to create sub-strings and sub-lists. Some example code
which creates sub-strings:
s := "abcd"
s[0..2] => "abc"
s[3..3] => "d"
s[0..<2] => "ab"

start := 0; end := 2
s[start..<end] => "ab"

We use .. to specify an inclusive end index, and ..< to specify an exclusive end index. Also note
how we can use any arbitrary expression with the range operators to define compact slice
expressions.
By convention Fantom APIs which support integer indexing allow the use of negative integers to
index from the end of the list. For example -1 can be used to index the last item of a list (or the last
character of a string). Using negative indexes works with all three shortcuts:
list := ["a", "b", "c", "d"]
list[-2] => evaluates to "c"
list[-1] = "last" => replaces list[3] with "last"
list[1..-1] => evaluates to ["b", "c", "last"]

Use of negative indexes applies to most methods on List and Str which take an index argument.

Bitwise Operators
Fantom doesn't have bitwise operators, instead normal method calls are used:
~a => a.not
a & b => a.and(b)
a | b => a.or(b)
a ^ b => a.xor(b)
a << b => a.shiftl(b)
a >> b => a.shiftr(b)

Serialization Expressions
Fantom supports three expression constructs which are designed to make the programming
language a true superset of the serialization syntax:
• Simples
• It-Blocks
• Collections

Simples
Simples are special serializable types which serialize via a string represenation. Fantom allows the
use of a simple expression:
<type>(<str>)

// for example:
Version("3.2")

// is syntax sugar for


Version.fromStr("3.2")

To use this expression, the type must have a static method called fromStr which takes
a Strparameter and returns an instance of itself. The method may contain additional parameters if
they have default values. The type does not have to implement the sys::Serializable facet to use
this expression (although it does if you want to serialize it). Simple expressions are a subset
ofconstruction calls.

It-Blocks
It-blocks enable you write compound expressions - they are typically used to initialize an instance.
This feature is a clean a superset of how complex types are serialized. An example it-block
expression:
Address
{
street = "123 Happy Lane"
city = "Houston"
state = "TX"
}

// is syntax sugar for (note: can't actually use it as param name)


Address.make.with |Address it|
{
it.street = "123 Happy Lane"
it.city = "Houston"
it.state = "TX"
}

Collections
It-blocks may also be used to initialize a collection if it supports a method called "add". Any
expression inside an it-block suffixed with a comma is assumed to be a call to it.add:
a, => it.add(a)
a, b, => it.add(a).add(b)
a, b, c => it.add(a).add(b).add(c)

Note the comma operator chains the calls to add, therefore the add method must return a chainable
type (typically This).
Here is an FWT example:
Menu
{
text = "File"
MenuItem { text = "Open"; onSelect=#open.func } },
MenuItem { text = "Save"; onSelect=#save.func },
}

// is syntax sugar for (note: can't actually use it as param name)


Menu.make.with |Menu it|
{
it.text = "File"
it.add(MenuItem { text = "Open"; onSelect=#open.func })
.add(MenuItem { text = "Save"; onSelect=#save.func })
}

Advanced Operators
Fantom has a couple other operators which will be discussed later:
• Closures are expressions which create a new function inside a method body.
• Call operator () is used to invoke a function variable.
• Dynamic invoke operator -> is used to call a method without compile time type checking.
• Field storage operator & is used to access a field's raw storage without going through its
getter/setter methods.
Statements

Overview
Fantom code is written as a series of statements just like Java or C#. However unlike Java/C#,
Fantom statements are not required to be terminated with a semicolon. A Fantom statement may
be terminated one of three ways:
1. newline
2. semicolon
3. end of block } curly brace
By convention a simple newline is the preferred mechanism for separating statements. A semicolon
is only used when placing multiple statements on the same line. For example these are all valid
Fantom statements:
if (authenticated)
{
sendToHomePage
return true
}

if (authenticated)
{
sendToHomePage();
return true;
}

if (authenticated) { sendToHomePage; return true }

The first version is the preferred syntax. The third version also exhibits good mojo if it is compact
enough to keep the code readable.
The Fantom grammar is not perfectly unambiguous when you omit semicolons. So on occasion
you will run into a situation when the compiler complains and you might need to stick in a
semicolon - but in practice this rarely happens. Another side effect of this is that Fantom requires
the opening parenthesis of a method call to be on the same line as the method identifier:
// ok
call(...)

// not ok
call
(...)

This rule also applies to the opening square bracket of an index operation:
// ok
list[...]

// not ok
list
[...]

Expression Statements
The most common type of statement is a stand alone expression. The following expressions are
considered valid statements:
• Any assignment expression including the increment/decrement operators
• Method calls (or chains of method calls)
Return Statement
The return statement is used to exit a method:
// if Void method:
return

// if non-Void method:
return <expr>

If the enclosing method is Void, then return simply returns control back to the code which called
the method. If the enclosing method is non-void, then the return statement includes the expression
used to return a result object to the caller.
If returning an expression, then the expression must start on the same line as the returnkeyword:
// this is ok
return call(x,
y, z)

// this is illegal
return
call(x, y, z)

Fantom allows you omit the return keyword if your method or closure contains exactly one
statement:
Str name() { return "Bob" } // long version
Str name() { "Bob" } // short version

Convention is to omit the return keyword for single statement returns.

Local Variables
Local variables may be declared using the following syntax:
// syntax
<type> <identifier> := <init>

// example
Str name := "Jack Shephard"

Fantom supports type inference for local variables, which allows you to omit the type signature. If
the type signature is omitted, then the variable is typed according to the initializer expression. In
the example above the right hand side resolves to a Str, so we could rewrite the statement above
as:
name := "Jack Shephard"

Fantom convention encourages use of type inference when possible. However if the right hand
side expression resolves to an ambiguous type, then you will need to specify the variable's type
signature. The most common case when type inference doesn't work is when you need to initialize
a local variable to null.
Fantom uses the := operator for local variable initialization rather than the standard = assignment
operator. The primary purpose of the := syntax is to distinguish normal assignments from local
declarations. This syntax captures programmer intent better and enables the compiler to catch
typos like a misspelled local.
Fantom does not support the comma operator to declare multiple local variables like Java or C#. In
practice though most locals will be declared using type inference.
If a local variable is not explicitly assigned an initial value, then it implicitly defaults to null, false,0,
or 0.0f following same rules for field defaults. This is a little different than Java or C# which require
definite assignment.

If Statements
Fantom supports if and else using the Java/C# syntax:
if (<cond>)
<block>

if (<cond>)
<block>
else
<block>

if (<cond>)
<block>
else if (<cond>)
<block>
else
<block>

The if condition must evaluate to a sys::Bool expression. The block can be a single statement or
a block of multiple statements delineated by { } curly braces.

Loop Statement
Fantom supports while and for loops using familiar Java and C# syntax. Although you'll find when
writing Fantom that most looping is actually done using closures.

While Statement
Fantom supports while loops using Java/C# syntax:
// syntax
while (<cond>)
<block>

// example
while (p != null)
p = p.next

The while loop executes its block until its condition evaluations to false. The while condition must
evaluate to a sys::Bool expression. The block can be a single statement or a block of multiple
statements delineated by { } curly braces.
Fantom doesn't currently support do while loops.

For Statement
Fantom supports for loops using Java/C# syntax:
// syntax
for (<init>; <cond>; <update>)
<block>

// example
for (i:=0; i<10; ++i)
echo(i)
The for condition must evaluate to a sys::Bool expression. The block can be a single statement or
a block of multiple statements delineated by { } curly braces. All three expressions of the forloop
are optional. If the cond expression is omitted, it defaults to true. The init expression is executed
once on loop entry and the update expression after each run of the loop. The for loop runs until
the condition evaluates to false.
Like Java/C# the init expression of the for loop can be a local variable declaration which defines a
local scoped only within the for loop. Unlike Java/C# the comma operator is not supported in the
init and update expressions.

Break Statement
The break statement is used with both the for and while loops to break out of the loop. For
example:
for (i:=0; i<10; ++i)
{
if (i == 3) break
echo(i)
}
echo("done")

// prints
0
1
2
done

The break statement always applies to the inner-most loop. Fantom does not support
labeledbreaks.

Continue Statement
The continue statement is used with both the for and while loops to jump back to the top of the
loop. For example:
for (i:=0; i<4; ++i)
{
if (i == 2) continue
echo(i)
}
echo("done")

// prints
0
1
3
done

The continue statement always applies to the inner-most loop. Fantom does not support
labeledcontinues.

Switch Statement
Fantom supports a switch statement used to execute a block of code by matching the value of a
expression against a list of case labels. The syntax is very similar to Java/C#:
switch (<cond>)
{
case <label1>:
<block1>
case <label2>:
<block2>
...
default:
<defaultBlock>
}

The condition expression is matched against all the case labels. If a match is found, then it
executes the statements in that case's block. If no matches are found then the
optional defaultblock is executed. Unlike the if, for, and while statements, the case blocks are
not wrapped with{ } curly braces.
Fantom's switch statement allows the condition and case expressions to be any valid expressions.
So you can switch on strings or types too. When the condition evaluates to
ansys::Int or sys::Enum and the case labels evaluate to constants, then the switch is compiled
into the tableswitch opcode. Otherwise the switch statement matches the case labels using an
equality check via the sys::Obj.equals method. For example:
// tableswitch
switch (weekday)
{
case Weekday.sat:
case Weekday.sun:
return "it's the weekend baby!"
default:
return "back to work!"
}

// equality switch
switch (method.upper)
{
case "GET": serviceGet
case "POST": servicePost
default: methodNotSupported
}

In Fantom the switch statement doesn't require a break to end a block of code for a given case like
Java or C#. You cannot fall-through from one case block to the next case block. However you can
group multiple case labels together like we did with Weekday.sat and Weekday.sun in the example
above.

Exception Handling
Fantom supports throw and try statements with a syntax very similar to Java and C#. These
statements are discussed separately in the exception handling chapter.
Exceptions

Overview
Fantom uses exceptions as the standard mechanism for error handling. An exception is a normal
object which subclasses from sys::Err. Exceptions are thrown or raised to indicate an error
condition. When an exception is raised, normal program execution is disrupted and the call stack is
unwound until an exception handler is found to handle the exception.
Fantom does not use Java styled checked exceptions where a method must declare the
exceptions it throws. In our experience checked exceptions are syntax salt which don't scale to
large projects. For example in Java the real exception is often wrapped dozens of times to make
the compiler happy as independent subsystems are integrated.

Err Class
All exceptions in Fantom subclass from sys::Err. Many APIs declare their own Err classes for
modeling specific error conditions. The type hierarchy is the primary mechanism used to classify
exceptions and is used to match exceptions to exception handlers.
All Errs also have a message and a cause. Message is a human readable Str to describe the error
condition. Cause is another Err instance which is the root cause of the exception. Cause is
often null if there is no root cause.
Fantom convention requires all exception classes end with Err and declare a constructor
calledmake which takes at least a Str message as the first parameter and an Err cause as the last
parameter. Typically both of these parameters have a default argument of null.
When an exception is thrown, the runtime captures the call stack of and stores it with
the Errinstance. You can dump the call stack of an exception using the trace method:
err.trace // dumps to Env.cur.out
err.trace(log) // dumps to log output stream

Throw Statement
The throw statement is used to raise an exception via the following syntax:
// syntax
throw <expr>

// example
throw IndexErr("index $i > $len")

The expression used with the throw keyword must be evaluate to a sys::Err type. When
thethrow statement is executed, the exception is raised and program execution unwinds itself to
the first matching exception handler.

Try-Catch Statement
Fantom uses the try-catch-finally syntax of Java and C# for exception handling:
try
{
<block>
}
catch (<type> <identifier>)
{
<block>
}
catch
{
<block>
}
finally
{
<block>
}

A list of catch blocks is used to specify exception handlers based on exception type. A catch block
for a given type will catch all raised exceptions of that type plus its subclasses. If an exception is
raised inside a catch block with no matching catch blocks, then the exception continues to unwind
the call stack (although if a finally block is specified it will be executed).
The blocks of code in a try can be either a {} block or a single statement. The identifier of each
catch block is used to access the exception caught - this variable is scoped within the catch block
itself.
You can have as many catch blocks as you like, however the type of a catch block cannot repeat a
type used previously. For example:
// this is legal
try {...}
catch (CastErr e) {...}
catch (NullErr e) {...}
catch (Err e) {...}

// this is illegal
try {...}
catch (Err e) {...}
catch (NullErr e) {...}

In the first block of code we first declare a catch handler for CastErr and NullErr which will catch
any exception of those types. We catch Err last which will catch anything else which is not
aCastErr or NullErr. However the second block of code will not compile because
the NullErr catch comes after the Err catch block (which includes NullErrs).
If you don't need to access the exception caught, you can also use a catch all block to catch all
exceptions using the following shorthand syntax:
try
{
return doSomethingDangerous
}
catch
{
return null
}

Finally Blocks
We use a finally block when we want to execute some code which runs regardless of how or if an
exception is handled. A finally block always executes when a try block exits. Finally is often
used to ensure that resources are cleaned up properly no matter what exceptions might occur. For
example the following code guarantees that the input stream is closed no matter what might go
wrong inside the try block:
void load(InStream in)
{
try
{
// read input stream
}
finally
{
in.close
}
}

Finally blocks have similar restrictions to C#. You cannot exit a finally block directly using
areturn, break, or continue statement.
Type System
Overview
The Fantom type system serves two primary purposes:
• Encapsulation: a mechanism to group fields and methods together
• Contracts: a mechanism to model semantics

Encapsulation
Types encapsulate a set of uniquely named slots. There are two types of slots: fields store state
and methods model behavior. Grouping a set of slots together enables us to create packaged units
of software which match our domain model.
This encapsulations serves several purposes:
• Contracts: it enables the explicit contracts discussed next
• Structure: it enables the three part namespace of pod::type.slot
• Inheritance: grouping slots together enable reuse through inheritance

Contracts
Types are also used to specify an explicit and implicit contract. The explicit contract specifies what
the class can do by the set of fields and methods it exposes. For example given asys::File, we
know that it will support an explicit set of methods like exists, isDir, and size. This set of methods
defines the contract for what we can and cannot do with a File. The compiler can use this
information to perform type checking and report errors if you are attempting to use unknown slots.
Sometimes you'll find compile time type checking gets in the way - in that case you simply switch
from the . operator to the -> operator to delay type checking until runtime (or do something clever
in your trap method).
The implicit contract specifies semantics that a human can understand - if I tell you a variable is
aFile, then you probably have a good understanding of what that variable is modeling.
Programming is largely about mapping a problem domain into code - type systems help us
annotate our code with domain specific terminology.

Types
There are two kinds of types in Fantom:
• Classes
• Mixins

Classes
Classes are the primary mechanism for specifying types. All objects are instances of exactly one
class which can be queried at runtime via the sys::Type.of method. Classes support
singleinheritance just like Java or C#.

Mixins
A mixin is a kind of type which is not designed to be used stand alone. Instead a mixin packages a
group of slots together to be inherited into a class (or another mixin).
Mixins are similar to interfaces in Java or C#, but much more flexible. A Java or C# interface is
purely a type definition of abstract methods, it can't actually include any behavior itself. Fantom
mixins can declare concrete methods which provide a lot more power.
You can't create instances of a mixin - they are an abstract type designed to provide reuse when
inherited into classes. Mixins also can't store state - although they can contain abstract fields to
define a type contract which requires a field signature.

Pure Object Oriented


Fantom is fundamentally an object-oriented language. It is "pure" in the sense that everything is an
object including core types such as Int. For example, Java primitive types such
as booleanand int do not subclass from java.lang.Object which creates a fractured type system.
Fantom defines a unified class hierarchy with sys::Obj as the root. Since everything is an object,
you can call methods on everything:
Type.of(false) => sys::Bool
1972.isEven => true
'x'.toChar => "x"

Arrays are another type system anomaly not supported by Fantom. For example, in Java arrays
are reference types which can be used as a java.lang.Object type, but they aren't proper classes
with nice OO methods. In most circumstances, the sys::List class is used instead of arrays. Plus
you will use sys::Buf instead of byte[] and sys::StrBuf instead of char[].

Nullable Types
Types may be nullable or non-nullable. A non-nullable type is guaranteed to never store the null
value. Nullable types are indicated with a trailing "?". This means non-nullable is the default unless
otherwise specified:
Str // never stores null
Str? // might store null

The compiler prevents obvious mistakes when using a nullable expression when a non-nullable
type is expected:
• null literal
• safe invoke method call or field access
• as operator
Additional checks are implicitly done at runtime when coercing a nullable type to a non-nullable
type. This allows your code to fail fast at the point where null bug was introduced versus
propagating into unrelated code.

Value-Types
The special types Bool, Int, and Float are value-types. These types are optimized by the runtime
to be passed by value instead of as a reference to an object. This allows Fantom to achieve the
same performance as using primitives in Java and value-types in C#.
Value-types can be nullable also. For instance a variable declared to be Int? can store null.
Value-types differ from reference types in that fields default to false/zero instead of null. However
the nullable versions of value-types do default to null.
A mapping of Fantom types to their runtime representations:
Fantom Default Java .NET
------ ------- ---- ------
Bool false boolean bool
Bool? null java.lang.Boolean bool?
Int 0 long long
Int? null java.lang.Long long?
Float 0.0f double double
Float? null java.lang.Double double?
By convention Fantom APIs use null to indicate an non-normal condition. For example, often in a
Java API which returns an int such as String.indexOf() or InputStream.read() a special value of
-1 will be used to indicate a non-normal result. This can be especially problematic when -1 is a
valid result. In Fantom APIs we return Int? and use null instead of a special value like -1.

Statically Typed
Fantom is statically typed - all method and fields signatures require type declarations. This is a
religious issue for many developers, but we believe type declarations just add too much value for
code analysis and readability to throw them out for a bit of code compression.
However there are definitely times when a static type system gets in the way of an elegant
solution. So Fantom provides some dynamic typing features too:
• the -> dynamic invoke operator lets you call any method with runtime checking
• The compiler will implicitly cast in most cases for you
• Type inference is supported for local variables, lists, and maps

Implicit Casts
Anyplace where a compile time type check would typically require a cast in Java or C#, the
compiler will implicitly insert a cast for you. The cast ensures that the JVM or CLR generates a
runtime exception if the type check fails. If the compiler knows that the types are incompatible, then
it will generate a compile time error.
Formally the rules are expressed as anytime where Type A is used and Type B is expected:
1. If A.fits(B) the call is statically known to be correct
2. Otherwise if B.fits(A) then we insert an implicit cast
3. Otherwise it is a compile time error
For example:
Int func(Int x) { ... }

Int i := 5
Num n := 5
Str s := "foo"

// statically correct as is: Int.fits(Int)


func(i) => func(i)

// implicit cast inserted: Int.fits(Num)


func(n) => func((Int)n)

// compile time error: !Int.fits(Str)


func(s) => error

This feature allows you to use Obj as a wildcard type which is assignable to anything. This is often
used with in conjunction with dynamic invokes which return Obj?:
Str name := x->person->name
if (test->isTrue) {...}
File(x->uri)

Coercion from a non-nullable type to a nullable type is safe. A coercion from a nullable type to a
non-nullable is implicitly allowed, but is checked at runtime:
Str? x := null
Str y := x // implicit cast as y := (Str)x

The above code will compile with the impilicit cast. However at runtime it will fail with a NullErr.
Type Signatures
We call the syntax used to express a type declaration a type signature. Type signatures are used
extensively in your source code, in the fcode formats, and in the reflection APIs. The formal
signature for a type is its qualified name or qname. Although in source code, we typically use the
simple name in combination with the using statement. There is also a special syntax for expressing
signatures of generic types.

Collections
There are two primary classes for managing collections: sys::List and sys::Map. Both of these
types have a special literal syntax and a special type signature syntax.

List
Lists are a sequential collection of objects with fast integer indexing. A Fantom list is very similar to
an ArrayList in Java or C# with similar performance tradeoffs: fast indexing and appending, but
slower inserts and removes from the middle. Lists have a literal syntax and a special type
signature syntax.

Map
Maps are a hashmap of key-value pair, very similar to an HashMap or Hashtable in Java or C#.
Maps have a literal syntax and a special type signature syntax.

Generics
Another feature eschewed by Fantom is user defined generics. We fall into that camp which finds
generics a wicked, complicated solution to a narrow range of problems. For example generics are
great for collections, but the mental gymnastics required to digest declarations likeClass Enum<E
extends Enum<E>> are too much for us.

Although there isn't a general purpose generics mechanism, Fantom does use generics in a limited
fashion. Specifically three classes use generics:
• List
• Map
• Func
These are the only three generic types in Fantom. Each generic type uses a set of generic
parameters in its method signatures. Generic parameters are always one of the following single
ASCII letters: A-H, L, M, R, and V. The meaning of each generic parameter is discussed below.
To use a generic we have to specify a type for each of the generic parameters - we call this
process parameterization. Fantom doesn't use a general purpose parameterization syntax
likeList<Str> as used by Java and C#. Instead each of the three generic types has its own custom
parameterization syntax discussed below.

List Type Signatures


The sys::List class uses two generic parameters:
• V: type of item stored by the list
• L: type of the parameterized list

The parameterization syntax of List is designed to mimic the array syntax of Java and C#:
// format
V[]
// examples
Str[] // list of Strs
Int?[] // list of Int?
Int[][] // list of Int[] (list of a list of Ints)

The L generic parameter is used to indicate the parameterized type itself. For example the
following is the signature of the sys::List.add method:
L add(V item)

Given type Str[], then V maps to Str and L maps to Str[]. So the add method for Str[] is
parameterized as:
Str[] add(Str item)

Map Type Signatures


The sys::Map class uses three generic parameters:
• K: type of key stored by the map
• V: type of value stored by the map
• M: type of the parameterized map

The parameterization syntax of Map is designed to mimic the map literal syntax:
// format
[K:V] // formal signature
K:V // brackets are optional in most cases

// examples
[Str:User] // map of Users keyed by Str
Str:User // same as above without optional brackets
Uri:File? // map of File? keyed by Uri
[Uri:File]? // map of Uri:File where the entire map variable might be null
Str:File[] // map of File[] keyed by Str
[Str:File][] // list of Str:File (brackets not optional)

The formal syntax for Map parameterization is [K:V]. Typically the brackets are optional, and by
convention left off. But in some complicated type declarations you will need to use the brackets
such as the [Str:File][] example above. Brackets are always used in APIs which return formal
signatures.

Func Type Signature


The sys::Func class uses nine generic parameters:
• A to H: the function parameter types
• R: the function return types

The parameterization syntax of Func is designed to match the syntax used by closures:
// format
|A a, B b ... H h -> R|

// examples
|Int a, Int b->Str| // function which takes two Int args and returns a Str
|Int, Int->Str| // same as above omitting parameter names
|->Bool| // function which takes zero args and returns Bool
|Str s->Void| // function which takes one Str arg and returns void
|Str s| // same as above, omitting optional void return
|->Void| // function which takes no arguments and returns void
|->| // shortcut for above
Function signatures are used extensively in functional programming and closures. It can be a bit
tricky to grasp at first, but what we are parameterizing is the sys::Func class itself - the arguments
passed to the function and the return type.
To understand this a bit better, let's consider a Java example. We often want to declare the type of
a "callback method" - in Java we typically do this by creating an interface. We then use that
interface type whenever we need to specify a method that requires that callback:
interface Comparator
{
int compare(Object a, Object b);
}

void sort(Comparator comparator)

In Fantom we skip the interface part and just declare the callback type using an in-place function
signature:
Void sort(|Obj a, Obj b->Int| comparator)

This signature says that sort takes one argument called comparator which references a Func that
takes two Objs and returns an Int.
But typically we are sorting a List which itself has been parameterized. List comes with a built-
insort method which has the actual signature:
L sort(|V a, V b->Int| c := null)

This method combines List's generic V parameter with a function signature. So given a list of
typeStr[], then the parameterized version of sort would be:
Str[] sort(|Str a, Str b->Int| c := null)

Function signatures are covered in yet more detail in the Functions chapter.
Compilation Units
Organization
Fantom source code for pods is organized into one or more directories on the file system. By
convention the source tree of a pod is structured as follows:
podxyz/
+- fan/ // ".fan" source files
+- java/ // ".java" Java native peers
+- dotnet/ // ".cs" C# native peers
+- res/ // resources to bundle with pod
+- test/ // ".fan" unit tests
+- build.fan // build script for pod

Each of those directories may contain arbitrary sub-directories under it, although it is not required.
Fantom source code is stored in plain text files using a ".fan" extension. Each source file can
contain one or more class or mixin definitions. Unlike Java, there is no restriction that each type
must be in its own source file. Although by convention we put each class into a source file with a
matching name. However if you have lots of little classes, then it is best to combine them
intelligently.

Char Encoding
There is only one simple rule to follow - all Fantom source files must be in UTF-8 encoding. Even
better, if you want to ensure that your source code is drop-dead easy to use in any editor then use
standard 7-bit ASCII characters (which is a clean subset of UTF-8). End of story, enough said.

Anatomy of Source File


Compiler writers like to use the fancy term compilation unit to describe the text of a source file. A
compilation unit has a pretty simple structure:
1. Zero or more using statements
2. One or more type definitions
The using statements import types from external pods into the compilation unit's namespace. The
type definitions are the actual classes and mixins.

Using
Every compilation unit can specify zero or more using statements to import types into the unit's
namespace. Importing types into the namespace lets you use simple type names in your type
signatures instead of qualified names.
The simplest form of the using statement is to import all the types of a pod into your namespace:
using inet

The statement above allows you to use any type defined in the inet pod via its simple name. For
example the simple type name TcpSocket will now resolve to inet::TcpSocket.
We can also import a single type into the namespace. For example to just
import inet::TcpSocketwithout importing all the other types from the inet pod we use this syntax:
using inet::TcpSocket
In large scale projects it is inevitable that two different pods will reuse the same type name. If we
need to use both types in a compilation unit, then we could use the qualified name - although it can
be a bit verbose. Another option is to use the using as statement. For example, given a naming
collision between red::Foo and blue::Foo, we can import blue::Foo as BlueFoo with this syntax:
using blue::Foo as BlueFoo

The using as statement is for naming collisions - don't use it for bone-headed things like
importing sys::Str as S (we've tried to keep key type names short to being with).
The sys pod is automatically imported into every compilation unit - in fact it is a compiler error if
you try to import sys explicitly.

Comments
Fantom supports three styles of comments:
• /* */block comments are used to comment out a block of text which can span multiple
lines. You can nest /* */ comments as a handy way to comment out large sections of code
• // end of line comments ignore everything to the end of the line
• ** fandoc comments
The ** comment is used for documentation. Like // comments, it comments everything to the end
of the line. Fandoc comments are similar to /** */ Javadoc or /// C# documentation comments.
You prefix one more lines of ** comments to specify the documentation for a type or slot definition:
** This class is really cool
class Cool
{
** When you gotta just do it!
Void justDoIt() {}
}

The documentation is written in plain text using a special set of rules for structure called fandoc.
Fandoc allows you to write documentation that looks good in plain text source files, but can be
translated into other formats such as nicely formatted HTML.
Pods
Overview
Pods are the top of Fantom's namespace as well as the unit of deployment. A pod's name is
globally unique and is used to organize the top level of Fantom's namespace. In this respect pods
serve the purpose of both a Java package and a JAR file (or .NET namespace and DLL).

Pod Meta
Pods do not use Facets for metadata like types and slots. Rather pod metadata is managed as
name/value pairs. Key metadata about your pod is defined in the build script:
class Build : build::BuildPod
{
new make()
{
podName = "myPod"
summary = "Summary description of my pod"
depends = ["sys 1.0", "web 1.0"]
meta = ["acme.sku": "0xdeadbeef"]
srcDirs = [`fan/`]
resDirs = [`locale/`]
}
}

During the build process fields like podName and depends are used by the compiler to generate the
pod's metadata. You can define your own additional name/value pairs with BuildPod.meta. Plus the
compiler will add its own metadata regarding when and where the pod was built.
Metadata is stored "/meta.props" in the pod zip file. The Pod.meta method is used to access a pod's
metadata at runtime. The following are the standardized keys:
• pod.name:name of the pod
• pod.version: Version of the pod
• pod.depends: list of semicolon separated Depend strings
• pod.summary: overview decription for pod
• pod.isScript: was the pod compiled in script mode
• pod.docSrc: should documentation include source code, see BuildPod
• pod.docApi: should pod be included in documentation, see BuildPod
• fcode.version: binary format version of the fcode
• build.time: compile time formatted as DateTime
• build.platform: compile env platform formatted as Env.platform
• build.host: compile env host name, see Env.host
• build.user: compile env user name, see Env.user
• build.compiler: compiler pod version
Classes
Overview
Classes are the most basic kind of type:
• Classes are scoped within a pod and globally identified with the qualified
namepodName::ClassName
• Classes contain zero or more uniquely named slots
• Classes always inherit from exactly one super class ('sys::Obj' is the sole exception to this
rule)
• Classes may inherit zero or more mixins
Classes are declared using the class keyword:
class MyNewClass {}

Class Modifiers
Classes may be annotated with the following modifiers:
• public
• internal
• abstract
• final
• const

For example to declare an internal abstract class:


internal abstract class Foo {}

Protection
The public and internal keywords define the visibility of the class. A public class may be used by
everyone in the system. An internal class is visible only to types within the declaring pod. If no
protection keyword is specified, the class defaults to public.

Abstract Classes
Abstract classes are classes designed never to be instantiated directly. It is a compiler error to call
the constructor of an abstract class. However abstract classes do have constructors for use by
their subclasses.
Abstract classes may or may not contain abstract methods. Although all classes which contain
abstract methods, must be declared abstract.
The opposite of an abstract class is called a concrete class.

Final Classes
Final classes cannot be subclassed. Any attempt to extend from a final class will result in a
compiler error.

Const Classes
Const classes are immutable - once created, an instance is guaranteed to have no state changes.
The following rules must be observed with const classes:
• A const class can contain only const fields or fields with no storage:
1. abstract fields
2. native fields (native code must ensure thread safety)
3. calculated fields
• A const class cannot contain any once methods
• A const class must inherit from a const class or sys::Obj
• A non-const class cannot inherit from a const class
Many fundamental primitive classes are const including Bool, Int, Float, and Str. Const classes
and immutability play an import role in thread safety.

Obj
The root of all classes is the sys::Obj class. Obj is the only class which doesn't have a superclass
itself.

Instances
Fantom uses a pure object oriented type system. This means that all variables are an object which
is an instance of exactly one class.

Reflection
The sys::Type class is used to represent a class at runtime. You can always get the class type of
an object using the sys::Type.of method:
Type t := Type.of(obj)

Since all objects are instances of a class, this Type object will always represent a class. Given
aType which represents a concrete class, you can create new instances reflectively:
t.make // use default make constructor
t.make(args) // use make constructor with arguments
t.method("makeX").call // lookup and use a named constructor
Mixins
Overview
Mixins are a mechanism to group a set of zero or more slots for reuse through inheritance. Mixins
are like Java/C# interfaces, but can contain concrete methods:
• Mixins are scoped within a pod and globally identified with the qualified
namepodName::MixinName
• Mixins contain zero or more uniquely named slots
• Mixins are implicitly abstract
• Mixins may inherit zero or more other mixins
• Mixins cannot declare concrete instance fields
• Mixins cannot declare constructors
• Mixins cannot declare once methods
• Mixins can declare abstract methods
• Mixins can declare concrete instance methods
• Mixins can declare static methods
• Mixins can declare abstract fields
• Mixins can declare static const fields
• Mixins can declare static constructors: static {}
Mixins are declared using the mixin keyword:
mixin MyNewMixin {}

To understand how mixins actually work under the covers, take a look at the tour on mixins to see
how mixins might look if translated to Java.

Mixin Modifiers
Mixins can be annotated with the public or internal modifiers just like classes.
A mixin can be declared with the const modifier which requires that any class using it must also
be const.
It is a compile time error to use the abstract or final modifier with a mixin.
Enums
Overview
Enums are a special type of class that define a discrete range of possible values:
• Enums are normal classes with all associated characteristics
• Enums are implied const
• Enums are implied final
• Enums have a fixed range of instances
• Enums can contain declare zero or more uniquely named slots
• Enums always always inherit from sys::Enum
• Enums may inherit zero or more mixins
• Enums must have private constructors
Enums are declared using the enum positional keyword and must declare their range of values
before any other slot declarations:
enum class Color { red, blue, green }

Range
The range of an enum is its discrete set of instances. Each instance of the range has the following
characteristics:
• is identified by a unique zero based ordinal
• is identified by a unique name
• is accessed via a static const field by its name
• is an instance of the declaring enum class
• is immutable (enums are implied const)
The compiler automatically generates two helper slots:
• vals is a list of all the values in the range indexed by ordinal
• fromStr is used to resolve by name

Consider this example to illustrate what happens under the covers:


// what you write
enum class Color { red, blue, green }

// what the compiler generates


class Color : Enum
{
static const Color red = make(0, "red")
static const Color blue = make(1, "blue")
static const Color green = make(2, "green")

static const Color[] vals = [red, blue, green].toImmutable

static Color? fromStr(Str s) { ... }

private new make(Int ord, Str name) : super(ord, name) {}


}
Enum Constructors
Enums can declare their own constructor to initialize additional fields. Remember that an enum is
const, which means that all its fields must also be const. This in turn means that all fields must be
initialized in the constructor. Enum constructors must be private. The following syntax is used to
declare and call an enum constructor:
enum class Suits
{
clubs("black"),
diamonds("red"),
hearts("red"),
spades("black")

private new make(Str color) { this.color = color; }

const Str color;


}

Note that the declaring class doesn't actually manage its own ordinals and names, this is always
done by the compiler.

Enum Modifiers
Enums can be annotated with the public or internal modifiers just like classes. It is a compile
time error to use the abstract, final, or const modifiers with a enum - final and const are
implied.

Reflection
Enums are keyed by both an zero based integer ordinal and a string name. The following
summarizes slots often used to work with enums (using Month as an example):
• sys::Enum.ordinal: the Int ordinal key
• sys::Enum.name: the Str name key
• sys::Month.vals: lookup instance by ordinal
• sys::Month.fromStr: lookup instance by name

Example code:
apr := Month.apr // direct access
jun := Month.vals[5] // lookup by ordinal
aug := Month.fromStr("aug") // lookup by name
Month.fromStr("bad") // throws ParseErr
Month.fromStr("bad", false) // returns null
Slots
Overview
Types are composed of uniquely named slots. Slots define the state and behavior of the object
oriented type. There are two types of slots:
• Fields define state
• Methods define behavior

Slot Modifiers
Slots may be annotated with one of the following modifiers:
• abstract: see fields and methods
• const: see fields
• new: see methods
• internal: see protection
• native: see fields and methods
• override: see fields and methods
• private: see protection
• protected: see protection
• public: see protection
• static: see fields and methods
• virtual: see fields and methods

Protection
A slot can be annotated with one of the following modifiers to define its visibility:
• public:everyone can access the slot
• protected: only subclasses can access the slot
• internal: only types within the declaring pod can access the slot
• private: only declaring type can access the slot

If no protection keyword is specified, the slot defaults to public.


Methods
Overview
A method is a slot which defines a function within a class or mixin:
class Boo
{
static Int add(Int a, Int b) { return a + b }
Int incr() { return count++ }
Int count := 0
}

In the example above add and incr are method slots on the class Boo. The incr method is an
instance method which means it is always invoked on an instance of Boo. The add method is static
and is not invoked on an instance:
b := Boo.make
x := b.incr()
y := Boo.add(3, 4)

Method invocation is performed using the . dot operator on a target. The target for instance
methods is an instance of the type; for static methods the target is the type name.
Methods in your own type (or types you inherit) are automatically scoped such that the target type
or instance is implied. For example:
class Foo : Boo
{
Int more() { return incr() + add(3, 4) }
}

If the method does not take any parameters, then we can leave off the () empty parenthesis. By
convention the empty parenthesis are always omitted:
b.incr // same as b.incr()

You can also the ?. operator to safely handle a null target. See safe invokes.

This
Instance methods always have an implied first parameter which is the instance itself identified via
the keyword this. The definitions of a and b are identical in the following example:
class Foo : Boo
{
Int a() { return incr }
Int b() { return this.incr }
}

Constructors
Constructors are special methods used to create new instances of a class. In Fantom, constructors
are named methods. The difference is that they use the new keyword in their definition instead of a
return type (the return type is implied to be an instance of the type):
class MissingPerson
{
new make(Str name) { this.name = name }
Str name
}
By convention, the primary constructor should be called make and other constructors should be
prefixed with make. Like other slots, constructors must be uniquely named within their type. To
create an instance, you call the constructor like a static method:
jack := MissingPerson.make("Jack Shephard")

You can also use the shorthand syntax for calling a constructor called "make":
sayid := MissingPerson("Sayid Jarrah")

From a client perspective, constructors look just like named factory methods (in fact switching
between a static method and constructor maintains source level compatibility, but not binary
compatibility). Constructors have the unusual property of acting like a static method on the outside
and an instance method on the inside. On the outside you call a constructor like a static method
and get back an instance of the type. On the inside of a constructor, the instance has already been
allocated and is available using the this keyword.
If you do not declare a constructor on your class, then the compiler will automatically generate a
public no arg constructor called make.
Only classes can have constructors. It is a compile time error to declare a constructor on a mixin.

Construction Calls
Fantom supports a special syntax called construction calls with the syntax Type(args). These calls
are resolved as follows:
1. bind to Type.fromStr if there is exactly one Str argument and Type.fromStr exists
(seesimple literals)
2. otherwise bind to Type.make
The construction operator works on static methods or constructors. So you can use it when makeis
a constructor or when make is a factory method. The method of construction call Foo(...) must
return Foo.
Convention is to always prefer a construction call to using make explicitly:
ArgErr.make // non-preferred
ArgErr() // preferred

ArgErr.make("bad arg") // non-preferred


ArgErr("bad arg") // preferred

Constructor Chaining
When creating subclasses, you must call one of your parent class constructors or another of your
own constructors using a syntax called constructor chaining. The syntax to call a parent
constructor is based on C++ and C# using the : after the formal parameters, but before the method
body:
class Foo
{
new make() {}
new makeName(Str name) {}
}

class Bar : Foo


{
new make() : super() {}
new makeFullName(Str? first, Str last) : super.makeName(last) {}
new makeLastName(Str last) : this.makeFullName(null, last) {}
}
All constructor chains start with the this or super keyword. Use this to chain to one of your own
constructors or super to call a parent constructor. Then the constructor to call is specified as a
normal method call with the name and argument list. As a shortcut, you can omit the name if the
parent constructor being called has the same name.
In the example above, Bar.make illustrates calling Foo.make- omitting the name implies calling a
parent of the same name - make in this case. Bar.makeFullName illustrates calling a super class
constructor by name. Bar.makeLastName shows how to call a peer constructor on your own class -
this is useful for ensuring all your initialization code is centralized in one constructor.

Static Constructors
Static constructors are methods executed to initialize the class itself. They are typically used to
initialize static fields. Static constructors use a Java like syntax:
class Foo
{
static { echo("initializing Foo...") }
}

Assignment to static fields is done in an auto-generated static initializer. It is permissible to have


multiple static initializers, in which case they are run in the order of declaration:
class Foo
{
static const Int a := 10
static { echo("1st a=$a b=$b") }
static const Int b := 20
static { echo("2nd a=$a b=$b") }
static { a = 30 }
static { echo("3rd a=$a b=$b") }
}

// outputs
1st a=10 b=null
2nd a=10 b=20
3rd a=30 b=20

Default Parameters
You can specify a default argument for parameters. Defaults can be applied to the last zero or
more parameters (right to left). For example:
static Int add(Int a, Int b, Int c := 0, Int d := 0)
{
return a + b + c + d
}

In this example the last two parameters c and d default to zero. This allows you to call
the addmethod with 2, 3, or 4 arguments:
add(3, 4, 5, 6)
add(3, 4, 5) // same as add(3, 4, 5, 0)
add(3, 4) // same as add(3, 4, 0, 0)

Operators
Fantom supports operator overloading using operator methods. Operator methods are just normal
methods which are annotated with the @Operator marker facet. The following naming conventions
are enforced for determining which operator is used by the method:
prefix symbol degree
------ ------ ------
negate -a unary
increment ++a unary
decrement --a unary
plus a + b binary
minus a - b binary
mult a * b binary
div a / b binary
mod a % b binary
get a[b] binary
set a[b] = c ternary

In the case of the unary and ternary operators the method name must match exactly. For the
binary operators, the method must only start with the given name. This allows binary operators to
be overloaded by parameter type:
class Foo
{
@Operator Int plusInt(Int x) { ... }
@Operator Float plusFloat(Float x) { ... }
}

Foo + Int => calls Foo.plusInt and yields Int


Foo + Float => calls Foo.plusFloat and yields Float

The compiler performs method resolution of operators using a very simple algorithm. If there are
multiple potential matches the compiler will report an error indicating the operator resolves
ambiguously. The compiler does not take class hierarchy into account to attempt to find the "best"
match.

Virtual Methods
Virtual methods are designed to be overridden by a subclass to enable polymorphism. Methods
must be marked using the virtual keyword before they can be overridden by subclasses.
Subclasses must declare they are overriding a method using the override keyword:
class Animal
{
virtual Void talk() { echo("generic") }
}

class Cat : Animal


{
override Void talk() { echo("meow") }
}

Animal().talk // prints generic


Cat().talk // prints meow

By default when a subclass overrides a method, it is implied to be virtual - its own subclasses can
override it again. You can use final keyword to prevent further overrides:
class Lion : Cat
{
override final Void talk() { echo("roar!") }
}

Abstract Methods
Abstract methods are virtual methods without an implementation. They are declared using
theabstract keyword. Abstract methods are implied to be virtual - it is an error to use both
theabstract and virtual keyword. Abstract methods must not provide a method body. If declared
within a class, then the containing class must also be abstract.
Once Methods
The once keyword can be used to declare once methods. A once method only computes its result
the first time it is called and then returns a cached value on subsequent calls. Once methods are a
great technique for lazily creating state without a lot of boiler plate code:
// hard way
Str fullName
{
get
{
if (&fullName == null) &fullName = "$firstName $lastName"
return &fullName
}
}

// easy way
once Str fullName() { return "$firstName $lastName" }

Restrictions for once methods:


• Must not be declared within a const class
• Must not be declared within a mixin
• Must not be a constructor
• Must not be static
• Must not be abstract
• Must return non-Void
• Must have no parameters
If a once method throws an exception, then there is no cached value - subsequent calls will re-
execute the method until it returns a value.

Covariance
Fantom supports covariance - which allows an overridden method to narrow the return type of the
inherited method:
abstract class Animal
{
abstract Animal mommy()
abstract Animal daddy()
}

class Cat : Animal


{
override Cat mommy() {...}
override Cat daddy() {...}
}

This Returns
A method declared to return This is a special case of covariance which always returns the type
being used. This technique is typically used by methods which return this to enable method
chaining. Consider this example:
class Connection
{
Connection open() { return this }
}

class MyConnection : Connection


{
MyConnection talk() { return this }
}

The APIs are written to allow method chaining, so we'd like to be able to write something like this:
MyConnection.make.open.talk

If you actually tried to compile that code you'd get an error like "Unknown slot Connection.talk". We
could write code without method chaining, or we could even use the "->" operator. But this
technique is so commonly used, that Fantom allows you to declare the return type as This:
class Connection
{
This open() { return this }
}

class MyConnection : Connection


{
This talk() { return this }
}

The This type is a special marker type like Void. It indicates that a method is guaranteed to always
return an instance of the target type. In our example above, the expression x.open will always
evaluate to an instance of Type.of(x).
Use of This is restricted to the return type of non-static methods. You can't use it for static
methods, parameter types, local variable types, or for fields. Overrides of a methods which
returnThis must also return This.

Dynamic Invoke
As any dynamic language proponent can tell you - sometimes static typing can be a real pain. So
Fantom supports a hybrid static/dynamic design by providing two call operators. The . dot operator
accesses a slot using static typing - if the slot cannot be resolved at compile time, then it results in
a compile time error.
The -> dynamic invoke operator lets you perform calls with no compile time type checking. What
dynamic invoke actually does it generate a call to the sys::Obj.trap method. By default
the trapmethod uses reflection to lookup and call the method. If the name maps to a field,
then trap will get or set the field depending on the number of arguments:
a->x a.trap("x", [,])
a->x = b a.trap("x", [b])
a->x(b) a.trap("x", [b])
a->x(b, c) a.trap("x", [b, c])

In the simplest case, the -> operator is syntax sugar to by-pass static type checking and use
reflection. But the ability to override the trap method is a powerful technique in the Fantom toolkit
for building dynamic solutions.
You can also the ?-> operator to safely handle a null target. See safe invokes.

Native Methods
Native methods are implemented in an alternate language which is "native" for each target
platform. Native methods are typically written in Java for the Java VM and C# for the .NET CLR.
Native methods use the native keyword and must not have a method body (like abstract methods).
The infrastructure for supporting native methods is discussed in the Natives chapter.
Fields
Overview
Fields are the mechanism used to manage state in classes. A single field encapsulates three
concepts:
1. Storage: a memory location to store a reference to the field's value
2. Getter: a method to get the field
3. Setter: a method to set the field
The key concept in Fantom is that all fields automatically use a getter and setter method (there are
a couple exceptions to this rule we will discuss below). In Java fields are simply a storage location.
Most Java programmers then wrap the field in a getter and setter method resulting in a huge
amount of bloated, boiler plate code. Then all access is done through a method call which is not
exactly the prettiest syntax for field access. C# is better in that properties are first class language
constructs. However management of storage is still done via a field and all the same boiler plate
code is required. These design patterns used in Java and C# don't capture programmer intent very
cleanly. The vast majority of these accessors are boiler plate code that don't actually do anything
other than get and set the field. Plus it requires multiple language constructs to model one logical
construct - this makes documentation a pain.

Simple Fields
In the simplest case, field declarations look just like Java or C#:
class Thing
{
Int id := 0
Str? name
}

In the example above id and name are instance fields on the class Thing. We use the := operator to
provide an initial value for the field which is automatically assigned before the constructor is
executed. If an initial value is not provided, then all fields default to null with the exception ofvalue-
types.
We access fields using the . operator:
thing := Thing()
thing.name = "bob"
echo(thing.id)
echo(thing.name)

In the code above, a getter or setter is not explicitly specified, so they are automatically generated
by the compiler. All access to the field is through the getter and setter method. So the
codething.name is really a method call to the name getter method. This enables you to later add an
explicit accessor without requiring a recompile of all the client code to your API.
You can also the ?. operator to safely handle a null target. See safe invokes.
Note: if you don't provide a getter or setter on a non-virtual field, then the compiler will optimize
field access internal to your pod. So inside your pod, accessor methods are only used if you
declare explicit accessors. Outside your pod, accessor methods are always used for non-const
fields. Abstract and virtual fields always use accessors.

Accessor Methods
The syntax for declaring a getter or setter is similar to C# properties:
class Thing
{
Int id := 0
{
get { echo("get id"); return &id }
set { echo("set id"); &id = it }
}
}

The accessor section is denoted by a {} block after the initial value assignment (or after the field
identifier if no initial value). You can declare both get and set - or if you just declare one, then the
other is automatically generated.
Inside the setter, the it keyword references the new value being assigned to the field. If you need
to use it inside a closure, then assign to a local variable:
set { newVal := it; 3.times { echo(newVal) }; this.&f = newVal }

The & storage operator is used to access the raw storage of the field (think about like C
dereference operator). The & operator is used when you wish to access the storage location of the
field without going through the accessor method. You can use the & operator in any of your class's
methods, but only the declaring class is permitted direct access using the & operator (it is like
storage is always private scoped). Inside the accessor itself you are required to use the &operator
(otherwise you would likely end up with infinite recursion).
Note that := field initialization always sets storage directly without calling the setter method
(seebelow for an exception to this rule).

Calculated Fields
In most cases, the compiler automatically declares storage memory for your field. However if the
compiler detects that a field's storage is never accessed then no memory is allocated. We call
these calculated fields. Calculated fields must declare both an explicit getter and setter which does
not use the & operator to access the field. Note that assigning an initial value using :=implicitly
uses the storage operator and will allocate storage.

Const Fields
The keyword const can be applied to your fields to indicate that the field is immutable:
class Thing
{
new make(Int id) { this.id = id }
const Int id
}

In the example above, id is an const instance field. Const instance fields can only be set in a
constructor. They work a little different than Java final fields - you can set them as many times as
you like (or let them default to null), but you can't assign to them outside of the construction
process.
Fantom also allows const instance fields to be set during the construction process via an it-block:
class Thing
{
new make(|This f|? f := null) { if (f != null) f(this) }
const Str name
}

t := Thing { name = "Brian" } // ok


t { name = "Andy" } // throws ConstErr
Inside of it-blocks, the check for setting a const field is moved from compile-time to run-time. The
compiler will allow any it-block to set a const field on it. However, if an attempt is made to call the
it-block outside of the constructor then ConstErr is thrown. This gives you flexibility to build APIs
which capture a snippet of code used to modify an immutable class.
Const instance fields may also configured during deserialization via
the sys::InStream.readObjmethod.
The value of all const fields must be immutable. This guarantees that all const fields reference an
instance which will not change state.
Const fields do not use accessor methods. It is a compile time error to declare accessors on a
const field. All field access is performed directly against storage.
Const fields cannot be declared abstract or virtual. However a const field can override a virtual
method with no parameters. But const fields cannot override fields since that would imply the field
could be set. Example of using this technique to use a pre-calcualted string for Obj.toStr:
const class Something
{
new make(...) { toStr = "..." }
override const Str toStr
}

Static Fields
The keyword static is used to create a static class level field:
class Thing
{
const static Int nullId := 0xffff
}

The field nullId is a const static field - there is only one global instance on the class itself. Const
static fields can only be set in static constructors. Static fields must be const - this ensuresthread
safety. Static fields are accessed using the . operator on the class itself:
Thing.nullId

Protection Scope
You can use the normal slot protection scope keywords with a field: public, protected, internal,
and private. The default is public if a keyword is omitted.
You can narrow the scope of the setter as follows:
class Thing
{
Int id { protected set }
Str name { internal set { &name = it } }
}

In the example above the fields id and name are publicly scoped. We've narrowed the scope of
theid setter to be protected. Because there is no method body for the setter of id, then the
compiler auto generates one. The declaration for name shows narrowing the scope and declaring
an explicit setter.
Only a protection scope keyword may be used with a setter. The getter is always the protection
scope of the field itself by definition. It is a compile time error to use keywords like const, virtual,
or abstract on an individual getter or setter.
Readonly Fields
It is quite common to publically expose a field's getter, but to hide the setter. This could be done by
narrowing the field's setter to be private as the following code illustrates:
class Thing
{
Int id { private set }
internal Int count { private set }
}

You can accomplish the same thing a little easier using the readonly keyword:
class Thing
{
readonly Int id
internal readonly Int count
}

The readonly syntax provides the exact same semantics as narrowing the setter to private. Note
that const fields are truly immutable - they always reference the same immutable instance.
Readonly fields do not imply anything about immutability - they only make the setter private.

Virtual Fields
Instance fields may be declared virtual which allows subclasses to override the getter and setter
methods:
class Thing
{
virtual Int id := 0
}

class NewThing : Thing


{
override Int id
{
get { echo("NewThing.id get"); return super.id }
set { echo("NewThing.id set"); super.id = it }
}
}

class AnotherThing : Thing


{
override Int id := 100
}

In this example Thing.id is virtual which allows subclasses to override its getter and setter method
(in this case Thing.id accessors are auto generated by the compiler). The classNewThing overrides
the accessors for id to add some tracing, then calls the superclass accessors. Note that the
overridden field must specify the override keyword and must be use the same type (covariance is
not supported).
Overridden fields are not given their own storage, rather they should delegate to their superclass
accessors using the super keyword. In the case that an override requires its own storage, you
should declare another field to use as storage.
In the example above, the class AnotherThing overrides Thing.id by providing a different default
value. Since no explicit getter or setter is specified, the compiler automatically generates
accessors. However the accessors for AnotherThing.id call super.id rather than access storage
directly. Also note that when an overridden field specifies an initial value, the field is initialized via
its virtual setter rather than direct storage.
Abstract Fields
A field may be declared abstract:
abstract class Thing
{
abstract Int id
}

class NewThing : Thing


{
override Int id := 0
}

An abstract field is basically just an abstract getter and setter that a subclass must override.
Abstract fields have no storage and cannot have an initial value. Overrides of abstract fields are
given storage accessed via the & operator (this is different than overriding a normal virtual field).

Definite Assignment
Non-nullable fields must be assigned in all code pathes. Each constructor which doesn't chain
tothis must set each non-nullable field which doesn't meet one of the following criteria:
• value type fields don't need to be set (Bool, Int, Float)
• fields with an initializer don't need to be set
• abstract or override fields don't need to be set
• native fields don't need to be set
• calcualted fields don't need to be set
Matching rules apply to static fields which must be set by all code paths in the static initializer.
As a special case, a constructor which takes an it-block as the last parameter allows definite
assignment to be postponed to runtime. The it-block is required to set every non-nullable field or
aFieldNotSetErr is raised at runtime. Consider this example:
class Foo
{
new make(|This| f) { f(this) }
Str name
}

Foo {} // throws FieldNotSetErr


Foo { name = "Brian" } // ok

Overriding a Method
A field's getter may be used to override a virtual method which has no parameters:
abstract class Named
{
abstract Str name()
}

class Person : Named


{
override Str name
}

In the example above Named.name is a virtual method that returns a Str. The class Person overrides
the Named.name method using a field. The override of Named.name maps to the getter of Person.name.
Note that the field must specify the override keyword and must have a matching type. Field
overrides of a method may use covariance - the field may be declared using a more specific type
than the method return type.
This design pattern is a handy technique to use in your Fantom code.

Mixins
A mixin can only declare const static fields and abstract fields. Mixins with abstract fields are
basically declaring a getter/setter signature. When implementing a mixin with abstract fields, it is
the subclass's responsibility to provide an implementation of the field. Field access of mixins uses
the standard . dot operator too.

Native Fields
Native fields are implemented in an alternate language which is "native" for each target platform.
Native fields are typically written in Java for the Java VM and C# for the .NET CLR. Native fields
use the native keyword and follow similiar rules to absract fields - no storage or accessors. The
infrastructure for supporting native fields is discussed in the Natives chapter.
Inheritance
Overview
Inheritance is the mechanism used to reuse existing types when defining a new type. We use
inheritance for two purposes:
• contract specialization
• implementation reuse

Contract Specialization
Types define an explicit and implicit contract. The implicit contract defines semantics to help
humans understand the code. The explicit contract is defined by the set of public slots. Let's look at
an example:
class File
{
virtual Int size() {...}
private Void checkNotDir() {...}
}

In the code above the class File declares a method called size which returns an Int. This is part of
the type's contract - given an instance of File, we always know there will be a method
calledsize that returns an Int for the number of bytes in the file.
On the other hand, the checkNotDir method is not part of the type's contract because it is private. It
is an implementation detail of the class, rather than a public API.
When we create a subclass of File, we are specializing the contract:
class HttpFile : File
{
Str:Str httpHeaders
}

By subclassing File, the HttpFile class inherits the contract of File and must support all the same
public slots. However we also specialize the base class by adding HTTP specific features such as
exposing the HTTP headers.

Implementation Reuse
If a type declares only abstract slots, then a subclass is inheriting purely the type contract - this is
what happens in Java or C# when declaring an interface. However, in Fantom both classes and
mixins can declare an implementation for their slots. Subclasses can then inherit the
implementation of their super type slots. We call this technique implementation reuse - it gives us a
convenient mechanism to organize our code and keep things nice and DRY.

Syntax
The syntax for inheritance is to include zero or more type definitions in the class declaration after a
colon:
// inheriting from Obj
class SubObj {}
class SubObj : sys::Obj {}

// class inheritance
class SubClassA : BaseClass {}
class SubClassB : MixinA, MixinB {}
class SubClassC : BaseClass, MixinA, MixinB {}

// mixin inheritance
mixin MixinC : MixinA {}
mixin MixinD : MixinA, MixinB {}

The order of the declaration does matter in some cases. If the inheritance types include a class
and one or more mixins, then the class type must be declared first.

Inheritance Rules
The following rules define how slots are inherited by a subtype:
1. Constructors are never inherited
2. Private slots are never inherited
3. Internal slots are inherited only by types within the same pod
4. All other slots are inherited
These rules follow the logic laid out in when discussing contract specialization. Private and internal
slots are implementation details, so they don't become part of the type's contract. Constructors are
always tied exactly to their declaring class, so they are not inherited either. These rules are applied
by both the compiler and the reflection APIs to determine the slot namespace of a given type.

Inheritance Restrictions
The inheritance rules listed above define which slots get inherited into a subtype's slot namespace.
Remember that a type's slots are keyed only by name, so under no circumstances can a type have
two different slots with the same name. Because of this axiom, there are cases which prevent
creating a subtype from conflicting super types:
1. Two types with static methods of the same name can't be combined into a subtype
2. Two types with const fields (either instance or static) of the same name can't be combined
into a subtype
3. Two types with instance slots of the same name and different signatures can't be combined
into a subtype
4. Two types with instance slots of the same name and same signature can be combined
provided the following holds true:
1. One is concrete and the other is abstract
2. Both are virtual and the subtype overrides to provide unambiguous definition
Using the rules above, Fantom avoids the diamond inheritance problem. First mixins can't declare
concrete fields, which mean they never store state. Second any ambiguity that arises from
diamond inheritance or otherwise requires the subclass to explicitly disambiguate (or if the
inherited slots are not virtual, then the subtype simply cannot be created).

Overrides
When inheriting slots from one or more super types, a type has the option to override any of the
super type's virtual slots. There are three mechanisms of override:
1. Method overrides Method (see virtual methods)
2. Field overrides Field (see virtual fields)
3. Field overrides Method (see overriding a method)

Covariance
Typically when overriding a slot, the signature of the override must match the super type's
signature exactly. However in some cases, the return type of a method may be narrowed - this
feature is called covariance. Covariance is a technique of specialization because the super type's
contract remains intact, we've only narrowed the contract of the subtype. The following details the
covariance support:
1. Method overrides Method: supported - details
2. Field overrides Field: unsupported
3. Field overrides Method: supported - details

Super
Often when overriding a method or field, it is desirable to call the super type's implementation. This
is done using the super keyword. There are two ways to use super:
// unnamed super
super.someMethod()

// named super
BaseType.super.someMethod()

The following rules define the use of super:


1. An unnamed super is always on the super class, never a super mixin
2. Obviously you can't use a named super on something which isn't one of your super types
3. Named supers allow you to jump up multiple levels of inheritance
4. Mixins cannot use unnamed super because that would be a non-virtual call on Obj (which is
itself really just a problem with Java's lame rules on invokespecial)
5. Mixins can use named supers on their super mixins
Facets
Overview
Facets are a mechanism to annotate types and slots with metadata. Facets are similar to
Javaannotations or C# attributes. A facet is a serialized instance of const class declared on a type
or slot which implements the sys::Facet mixin.

Facet Classes
Facets are defined as normal classes using the facet positional keyword:
// simple marker facet with no fields
facet class Indexed {}

// struct facet with one or more const fields


facet class Table
{
const Str name := ""
const Bool autoCreate
}

All facets classes automatically inherit the sys::Facet mixin. Facets are implied to be const
classes which means all their fields must be const.
Facet classes are not allowed to define their own constructor. Instead the compiler will generate
one for you. If your facet has fields, then it is implied to be Serializable and all its fields must be
serializable types.
The classes above will have the following synthetic definitions:
// marker facet with no instance fields is singleton
const class Indexed : Facet
{
static const Indexed defVal := Indexed()
private new make() {}
}

// struct facet with instance fields gets it-block constructor


@Serializable
const class class Table : Facet
{
new make(|This|? f) { f?.call(this) }
}

Annotations
Any type or slot can be annotated with facets using the @ symbol:
@Indexed
@Table { name = "Employees"; autoCreate = true }
class Employee {}

Struct facets assign their field values inside curly braces similiar to an it-block. Only simple literal
expressions may be used in facet annotations which the compiler can determine how to serialize.

Reflection
Facets are available at runtime via the following methods:
• Type.facet
• Type.facets
• Type.hasFacets
• Slot.facet
• Slot.facets
• Slot.hasFacets

Always prefer the facet and hasFacet methods over facets since it is much more efficient. Some
examples:
// check if a type is serializable, returns null if not
Serializable? ser := obj.typeof.facet(Serializable#, false)

// check if a field is transient


field.hasFacet(Transient#)

Inheritance
You can annotate a facet class with the sys::FacetMeta facet to declare that a type-level facet
should be inherited:
@FacetMeta { inherited = true }
facet class F {}

@F class A {}
class B : A {}

In the example above the class B will inherit the facet F. Facet inheritance works on any type (class
or mixin) in the inheritance hierarchy.
Functions
Overview
Functions are first class objects in Fantom modeled via the sys::Func class. Functions have a
signature formally defined as a set of parameter types and a return type. Functions which don't
return an object have a Void return type. Functions are created one of three ways:
1. Methods: all methods are really wrappers for a function
2. Closures: closures are expressions which result in a function
3. Bind: the Func.bind method is used to create a new function by binding arguments to an
existing function

Function Signatures
The Func class is a built-in generic type, with a custom parameterization syntax:
// format
|A a, B b ... H h -> R|

// examples
|Int a, Int b->Str| // function which takes two Int args and returns a Str
|Int, Int->Str| // same as above omitting parameter names
|->Bool| // function which takes zero args and returns Bool
|Str s->Void| // function which takes one Str arg and returns void
|Str s| // same as above, omitting optional void return
|->Void| // function which takes no arguments and returns void
|->| // shortcut for above

The examples above are type signatures much like you'd use Str or Str[].

Calling Functions
Functions are just objects like everything else in Fantom. All functions subclass the Func class
which provides methods for invoking the function. The most basic method is Func.callList which
takes a list of arguments and returns the result (or null if the return if void):
// func is a function which takes two Int args and returns an Int
|Int a, Int b->Int| func
func.callList([7, 8])

The Func class also supports the call method for calling with different arity (zero through eight).
For example to call the function above with two arguments:
func.call(7, 8)

Using the call arity versions provides better performance in most cases because it skips
packaging up the arguments in a list.
Fantom also supports a bit of syntax sugar to call a function like a normal method call using
the() operator. For example we could call the function above using this syntax:
func(7, 8) // syntax sugar for func.call(7, 8)

Type Compatibility
Functions have some special rules when it comes to type compatibility. The axiom for type
compatibility is that type A is compatible for type B if A can be used whenever B is expected. Most of
the time this means A extends from B through inheritance. For example Int is type compatible
with Num because anyplace Num is expected, we can pass an Int.
A type declaration for a function means "these are the are the arguments I'm going to pass to this
function and the result I expect back". So function type A is compatible with function type B if A can
accept the arguments which B declares it is going to pass and returns an expected type.
Specifically, function type A is compatible with function type B if these rules apply:
1. A declares the same number or less parameters than B
2. Each parameter type in A is compatible with its respective parameter type in B
3. A returns a type compatible with B (or if B returns void, then A can return anything)
The following table illustrates some examples which shows what Type.fits would report:
Num fits Int => false
Int fits Num => true

|Int a| fits |Int a| => true


|Num a| fits |Int a| => true
|Int a| fits |Num a| => false

|Int a| fits |Int a, Int b| => true


|Int a, Int b| fits |Int a| => false

|->Void| fits |->Int| => false


|->Int| fits |->Void| => true
|->Int| fits |->Num| => true
|->Num| fits |->Int| => false

The first two items in the table above are for reference - Int fits a Num, but not vise versa. Next let's
look closely at this example:
|Num a| fits |Int a| => true

What this shows is that if a function type is declared to take an Int, we can pass a function that
accepts a Num. That may seem counterintuitive at first, but remember that functions are the flip side
of normal type checking. Here is a concrete example of that concept in terms a typical Java or C#
programmer might find more natural:
class WidgetEvent {}
class ButtonEvent : WidgetEvent {}
addButtonListener(|ButtonEvent evt| callback)

In the code above ButtonEvent is a subclass of WidgetEvent. We've got a method which registers a
callback to invoke when a button is pressed - the argument passed to the callback will be
aButtonEvent. However, if we happen to have a function that accepts any WidgetEvent, then it will
quite happily accept ButtonEvent arguments:
static void anyWidgetCallback(WidgetEvent evt) { ... }

button.addButtonListener(&anyWidgetCallback)

This is what is meant by functions being the "flip side" of normal type checking. Where normal type
checking accepts any specialization of a type, function type checking accepts anygeneralization of
a function.

Arity Compatibility
Next let's look at how arity (number of parameters) figures into functional type compatibility by
dissecting these examples:
|Int a| fits |Int a, Int b| => true
|Int a, Int b| fits |Int a| => false
Here we see that a function that accepts one Int is compatible with a function type that generates
two Ints. This is an ability of all functions in Fantom - to accept more arguments than they will use.
It is kind of like default parameters in reverse. We use this technique all the time in the core
classes. For example the Map.each method is used to iterate the key/value pairs:
// actual signature of Map.each
Void each(|V value, K key| c)

// iterate with function that only accepts value


map.each |Obj value| { echo(value) }

// or iterate with function that accepts both value and key


map.each |Obj value, Obj key| { echo("$key = $value") }

Many of the APIs which accept a function will pass multiple parameters, but you don't actually have
to use all of those parameters.

Methods
In Fantom, all methods wrap a function accessed via the Method.func method. The Func for a
method serves as its reflective handle. This relationship between functions and methods is a key
aspect of how Fantom bridges object oriented and functional programming (the flip side is that all
functions are an object).
Mapping static methods to functions is straight forward:
static Int add(Int a, Int b) { return a + b }

func := type.method("add").func
nine := func(7, 2)

One gotcha to be aware of - you can't access the Method.func method without parenthesis, and
then use the parenthesis to invoke the function because the parenthesis will bind to
theMethod.func call:
type.method("add").func // returns Func
type.method("add").func() // same as above

type.method("add").func().call(7,2) // invoke function


type.method("add").func()(7,2) // same as above

Instance methods map to a function where the first argument is the implicit this parameter. If
you've ever used Python this concept is pretty much in your face with the explicit self argument.
Fantom lets you use instance methods like Java or C#, but we still need to map those OO methods
to functions. Let's consider this example:
m := Str#replace
f := m.func

// note method params does not include the


// implicit this, but the function params does
m.params => [sys::Str from, sys::Str to]
f.params => [sys::Str this, sys::Str from, sys::Str to]

// both of these result in "hello!"


s1 := "hi!".replace("hi", "hello")
s2 := f("hi!", "hi", "hello")

The code above gets the Str.replace instance method as a function. The replace method takes
two string arguments, but when flattened into a function it takes three string arguments because
we have to account for the implicit this argument.
Immutable Functions
An immutable function is one proven to be thread safe. You can check immutability at runtime
viasys::Obj.isImmutable and attempt to convert a function via sys::Obj.toImmutable. Immutable
functions are often required when working with actors.
Immutability works as follows:
• Method functions are always immutable - see sys::Method.func
• Closures which only capture final, const variables are always immutable; toImmutable
always returns this
• Closures which capture non-final or non-const variables are always mutable; toImmutable
always throws NotImmutableErr
• Closure which capture non-final variables which aren't known to be immutable until runtime
(such as Obj or List) will return false for isImmutable, but will provide a toImmutable method
which attempts to bind to the current variables by calling toImmutable on each one
• Functions created by Func.bind are immutable if the original function is
immutable andevery bound argument is immutable
The definition of a final variable is a variable which is never reassigned after it is initialized. Any
variable which is reassigned is considered a non-final variable.
Closures
Overview
Closures are an expression to create a function inside the body of a method. Closures have the
ability to reference local variables from their enclosing scope. This ability to create inline functions
which access local scope makes it easy to use closures as method arguments. For instance
closures are used extensively as an iteration mechanism.

Syntax
The basic syntax of a closure:
|A a, B b...->R| { stmts }

The start of a closure is its signature which reuses the same syntax as function types. The body of
the closure is a series of zero or more statements. The return statement is used to return a result
and exit out of the closure (Fantom doesn't support any other way to jump out of a closure other
than return or throw). Let's look a simple example:
f := |->| { echo("hi there") }
f()
f()

The code above creates a closure that prints a message to the console. If we run the code above
"hi there" is printed twice. We are assigning the closure to the variable f. The closure itself is an
expression which creates an instance of Func - just like 8 is an expression which creates an Int.
The signature of the function is |->| which means that the function takes no arguments and
returns Void. Once the closure is assigned to f, we can call f like any other function.
Here is another example:
f := |Int a, Int b->Int| { return a + b }
nine := f(4, 5)

The code above declares a closure which accepts two Ints and returns their sum. Notice the
closure uses the return statement to return the result (later we'll see how we can omit it).

Binding Locals
The real power of a closure is its ability to bind to the local variables in its enclosing scope.
Consider this example:
counter := 0
f := |->Int| { return ++counter }
echo(f())
echo(f())
echo(f())
echo(counter)

This example creates a function which returns an Int and then calls the function three times. Note
how the body of the closure uses the local variable counter. The closure has access to both read
and write any variable in its enclosing scope - just like an if statement or a while loop. So the
output of the code above is to print "1", "2", "3", and "3".
Scope Lifetime
When a closure binds to a local variable in its outer scope, that variable lives as long as the closure
lives. Remember that closures are just Func objects which can be passed outside of the original
scope. Consider this example:
static Func createFunc()
{
counter := 0
return |->Int| { return ++counter }
}

static Void main()


{
f := createFunc
echo(f())
echo(f())
echo(f())
}

The createFunc method returns a closure function bound to the local variable counter. The local
variable will exist as long as the closure exists. In this case the main method assigns the function to
the variable f then calls it three times. The output will print "1", "2", and "3". Effectively this allows
closures to store their own state between invocations.

Binding This
If a closure is declared inside an instance method, then a closure can bind this variable just like
any other local:
Str first := "Bart"
Str last := "Simpson"

Void test()
{
f := |->Str| { return first + " " + this.last }
echo(f())
}

The code above illustrates binding to two local slots. The closure binds to first with an
implicitthis. The closure uses an explicit this to bind to last. Note that the this keyword
references the enclosing method's instance, not the the closure object. This also means
generic Obj methods like toStr and type reference the enclosing method instance, not the closure
instance.

Multiple Closures
When a method declares multiple closures, the closures all share the same local variables:
counter := 0
f := |->Int| { return ++counter }
g := |->Int| { return ++counter }
echo(f())
echo(g())
echo(f())
echo(g())

The code above prints "1", "2", "3", "4" because both f and g share the same binding to counter.
Note: in the current implementation all closures share the same set of locals. This means that any
closure holding a reference to those locals will prevent garbage collection of all closure variables.
Closure Parameters
A closure is just a normal expression and can be passed as an argument to a method call which
expects a Func parameter. Many key APIs are designed to work with functions. For example
consider the List.findAll method which returns a sub-list of every item matching a criteria. Since
we want to leave the match criteria open ended, findAll lets you pass in an arbitrary function to
determine matches.
Let's consider an example for finding all the even numbers in a list:
list := [0, 1, 2, 3, 4]
f := |Int v->Bool| { return v%2==0 }
evens := list.findAll(f)

The code above creates a function, then passes it to the findAll method. Since the closure is just
an expression we could also rewrite the code as:
evens := list.findAll(|Int v->Bool| { return v%2==0 })

Since closures are used heavily in this way, Fantom supports a special syntax borrowed from
Ruby. If a closure is the last argument to a method call, then the closure can be pulled out as a
suffix to the call:
evens := list.findAll() |Int v->Bool| { return v%2==0 }

Since we aren't passing any arguments other than the closure we can simplify this code even
further by removing the parens:
evens := list.findAll |Int v->Bool| { return v%2==0 }

Iteration
Closures are designed to be the primary mechanism of iteration. Key methods which accept a
function parameter:
• sys::List.each: iterate a list
• sys::List.eachr: reverse iterate a list
• sys::Map.each: iterate a map

When iterating a list both the value and the integer index are passed to the closure:
list := ["one", "two", "three"]
list.each |Str val, Int index| { echo("$index = $val") }

But remember that we don't have to use all the arguments provided to the function. For example if
we don't care about the integer index:
list := ["one", "two", "three"]
list.each |Str val| { echo(val) }

Map iteration works the same way:


map := [1:"one", 3:"three", 5:"five"]
map.each |Str val, Int key| { echo("$key=$val") }
map.each |Str val| { echo(val) }

Closure Type Inference


Closures which are passed as the last argument to a method support type inference:
// fully specified closure signatures
list := ["one", "two", "three"]
list.each |Str v, Int i| { echo("$i = $v") }
list.each |Str v| { echo(v) }

// inferred closure signatures


list.each |v, i| { echo("$i = $v") }
list.each |v| { echo(v) }

If you leave the types off the closures parameters, then they are inferred based on the closure's
context. In the example above a closure passed to Str[].list.each is inferred to have a type of|
Str,Int|.

You can also use inference in conjunction with a return type or you can omit the return type
entirely:
odds = [1, 2, 3, 4, 5].findAll |v->Bool| { v.isOdd }
odds = [1, 2, 3, 4, 5].findAll |v| { v.isOdd }

Closures can only infer the type when they are being passed to a method which expects a function.
If a closure's parameters cannot be inferred then the defaults to Obj?:
// closure with inferred type of |Obj? v|
f := |v| { echo(v) }

It-Blocks
It-blocks are a special form of closures with the following differences:
• They omit a function signature and are declared only with curly braces
• Use type inference based on their context
• Define an implicit single parameter called it
• Define an implicit scope for it
• Return keyword is not allowed in an it-block
• It-blocks are given compile time permission to set const fields on the it parameter,
although runtime checks will throw ConstErr if an attempt is made to set a const field
outside of its constructor (see const fields)
An it-block can be used whenever a single parameter function is expected:
["a", "b", "c"].each |Str s| { echo(s.upper) } // long hand
["a", "b", "c"].each { echo(it.upper) } // short hand

In the example above, the it-block is a closure with an implicit Str parameter called it.
The it parameter works just like the implicit this parameter in an instance method. If a given
identifier is not declared in the local scope, then we attempt to bind to it:
["a", "b", "c"].each { echo(it.upper) } // explicit it call
["a", "b", "c"].each { echo(upper) } // implicit it call

Just like this, if a local variable shadows a slot on it, then the local variable is used. If an attempt
is made to implicitly access a slot which exists on both this and it, then it is a compile time error:
["a", "b", "c"].each { echo(toStr) } // Ambiguous slot error
["a", "b", "c"].each { echo(it.toStr) } // explicit call on it
["a", "b", "c"].each { echo(this.toStr) } // explicit call on this

This Functions
As a general rule the sys::This type is reserved for use only as the return type of instance
methods. There is one exception - you are allowed to declare a method parameter typed as|
This| to indicate that an it-block function is expected:
new make(|This| f) { f(this) }
With-Blocks
Fantom allows you to append an it-block to any expression. Whenever an it-block is used and a
function is not expected, then the compiler generates a call to Obj.with:
list := Str[,].with { fill("x", 3) } // explicit call to with
list := Str[,] { fill("x", 3) } // implicit call to with

The default implementation of Obj.with just applies the function:


virtual This with(|This| f)
{
f(this)
return this
}

Using it-blocks and Obj.with allows you open a new lexical scope with any expression. It is quite
useful for declarative programming.
Java FFI
Overview
The Java Foreign Function Interface (or Java FFI) is a feature which allows Fantom code to easily
utilize normal Java libraries. The Java FFI is basically a mapping of the Java type system into the
Fantom type system:
• Java packages => Fantom pods
• Java classes => Fantom classes
• Java interfaces => Fantom mixins
• Java fields => Fantom fields
• Java methods => Fantom methods
Fantom was designed to run on the JVM, so mapping between the two worlds is fairly straight
foward with a high level of interoperability. However, there is a level of impedance mismatch
between the two type systems. Features supported by the Java FFI:
• Interop with any Java API except those which use multi-dimensional arrays
• Static type checking
• Call overloaded Java methods from Fantom
• Construct Java classes using the Fantom constructor syntax
• Extend Java classes (only one level of inheritance allowed right now)
• Implement Java interfaces
• Implicit coercion between Java primitives and sys::Int, sys::Float
• Implicit coercion between one-dimensional Object arrays and sys::List
• Direct mappings for Java one-dimensional primitive arrays
• Fantom reflection support for Java members
• Dynamic invoke support against Java classes
Features which are not yet available in Java FFI:
• Multi-dimensional arrays
• Subclassing a Java class more than one level deep
• Attempting to override a Java overloaded method; this means you cannot subclass or
extend from a type with abstract overloaded methods
Features which are not supported result in a compile time error.

Interop Summary
The following table summarizes the mapping of Java types to Fantom types:
Java Type Fantom Type
----------- -----------
foo.bar.Baz [java]foo.bar::Baz
boolean sys::Bool
byte sys::Int
short sys::Int
char sys::Int
int sys::Int
long sys::Int
float sys::Float
double sys::Float
java.lang.Object sys::Obj
java.lang.String sys::Str
java.lang.Boolean sys::Bool?
java.lang.Long sys::Int?
java.lang.Double sys::Float?
java.math.BigDecimal sys::Decimal
Foo[] Foo[] // sys::List parameterized with Foo
boolean[] [java]fanx.interop::BooleanArray
byte[] [java]fanx.interop::ByteArray
short[] [java]fanx.interop::ShortArray
char[] [java]fanx.interop::CharArray
int[] [java]fanx.interop::IntArray
long[] [java]fanx.interop::LongArray
float[] [java]fanx.interop::FloatArray
double[] [java]fanx.interop::DoubleArray
Foo[][] // unsupported for both primitivies and Objects

Quick reference for mapping Java code to Fantom code:


Java Fantom
-------------------------- --------------------------
import javax.swing using [java] javax.swing
import java.util.Map.Entry using [java] java.util::Map$Entry as Entry
JFrame f = new JFrame(...) f := JFrame(...)
array.length array.size
array[i] array[i]
array[i] = val array[i] = val
int[] x = new int[5] x := IntArray(5)

How it Works
The Java FFI does not use any special Fantom syntax. Java APIs are imported into the Fantom
type system via the normal using statement with a special syntax for pod names. Java packages
are mapped to Fantom pods by prefixing the string "[java]". For example the Java
packagejavax.swing has the Fantom pod name of [java]javax.swing.
The Fantom compiler itself has no knowledge of Java libraries, rather it supports FFI plugins based
on pods being prefixed with "[ffi]". In the case of the Java FFI, the compilerJava pod is the compiler
plugin for importing the Java type system into Fantom.
Fantom code using the Java FFI results in a normal pod file compiled down into fcode. The only
difference is that the fcode contains type and member references which are Java specific. This
means that Fantom pods with Java FFI calls are not necessarily portable; attempting to use a Java
FFI call on .NET will fail.

Class Path
The current implementation of the compilerJava plugin is pretty simple. It looks for packages and
classes using the Fantom runtime classpath. This includes:
1. jars found in "sun.boot.class.path"
2. {java}lib/rt.jar (only if step above fails to find anything)
3. {java}lib/ext/*.jar
4. {fan}lib/java/ext/*.jar
5. {fan}lib/java/ext/{Env.platform}/*.jar
6. jars found in "java.class.path" system property
In order to use a class found in the classpath, compilerJava uses Java reflection and resolves the
class via Class.forName with the current classloader. If you have problems importing classes you
can dump the jars and packages found using:
fan compilerJava::ClassPath

Primitives
The special Java primitives boolean, long, and double are implicitly mapped to the Fantom
typessys::Bool, sys::Int, and sys::Float respectively. The other Java primitives are mapped as
follows:
byte sys::Int
short sys::Int
char sys::Int
int sys::Int
long sys::Int
float sys::Float

The special primitives above are not directly supported by the Fantom type system. Therefore you
cannot use them as local variables or in field or method signatures. They are always coerced
to/from their Fantom representation.

Reflection
All Java objects maintain a sys::Type represtation to provide Fantom style reflection. You can use
a dynamic call to toClass to get the java.lang.Class of a sys::Type:
ArrayList# // evaluates to sys::Type
ArrayList#->toClass // evaluates to java.lang.Class

Arrays
Arrays of Objects are implicitly boxed/unboxed as Fantom sys::List. If you call a method which
returns an array of Objects it is boxed into a Fantom list of the appropriate type. Likewise you pass
a Fantom list of the appropriate type whenever a Java array is expected.
Primitive arrays are handled specially without any boxing/unboxing. They are represented in the
Fantom type system via the special types:
boolean[] [java]fanx.interop::BooleanArray
byte[] [java]fanx.interop::ByteArray
short[] [java]fanx.interop::ShortArray
char[] [java]fanx.interop::CharArray
int[] [java]fanx.interop::IntArray
long[] [java]fanx.interop::LongArray
float[] [java]fanx.interop::FloatArray
double[] [java]fanx.interop::DoubleArray

The make, get, set, and size methods provide symbolic representations for working with primitive
arrays. They are mapped directly to a single opcode in the Java bytecode:
int[] x = new int[4] => x := IntArray(4)
x[2] => x[3]
x[3] = 5 => x[3] = 5
x.length => x.size

Nullable
Any Java API which uses reference types are mapped into the Fantom type system as nullable:
// Java methods
String foo(Object x, int y, Foo z)
void bar(String[] x) {}

// Fantom representation
Str? foo(Obj? x, Int y, Foo? z)
Void bar(Str?[]? x) {}

Note the case of String[] we assume that the entire array could be null or that any of the array
items may be null, so the Fantom mapping is Str?[]?.
Overloaded Methods
One impedance mismatch between Java and Fantom is that Java permits a field and method to
have the same name. Java also allows method overloading where multiple methods with the same
name may be declared with different parameter types.
Fantom only allows a single definition of a slot for a given name. However when calling out to Java
types the compiler will correctly resolve overloaded fields and methods. Let's consider this Java
class:
class Foo
{
String a;
String a() { return a; }
void a(String x) { a = x; }
String b() { return a; }
}

In the class above a is overloaded by a field and two methods. Let's look at how Fantom code is
resolved against the Java members:
foo.a // lack of parens indicates field get
foo.a = "x" // field set
foo.a() // call Foo.a() method
foo.a("x") // call Foo.a(String) method
foo.b() // call Foo.b() method
foo.b // call Foo.b() method - no ambiguity so we can omit parens

Resolving a call to an overloaded version of the method follows the same rules as the Java
Language Specification. It is a compile time error if the arguments do not match any methods or if
they match multiple methods ambiguously.

Constructors
Under the covers Fantom treats Java constructors just like the Java VM treats them - as special
methods with the name of <init>. The standard constructor syntax is used to invoke a Java
constructor:
a := Date()
b := Date(millis)
c := Date(2008-1900, 11, 13) // crazy API to create 13-Dec-2008

The constructor call is resolved against the Java constructors using the same rules for
resolvingoverloaded methods.

Subclassing
The Java FFI enables you to both extend from a Java class and implement Java interfaces. For
example to create a subclass of java.util.Date:
using [java] java.util::Date as JDate
class FanDate : JDate
{
new now() : super() {}
new make(Int millis) : super(millis) {}
}

The standard inheritance syntax is used to extend and implement Java types.
The Fantom subclass must define how the Fantom constructors call the Java superclass
constructors. This is done by calling super as an overloaded constructor (if the base class has
multiple constructors). You may not use a this constructor chain.
Constructors for a Fantom class which subclasses from a Java class are emitted as true Java
constructors. Therefore you may not declare two Fantom constructors with the same parameter
types as it would result in duplicate Java constructors. For example the following code would result
in a compile time error:
class FanDate : Date
{
new now() : super() {}
new epoch() : super(0) {}
}

Because of this difference between Fantom constructors versus Java constructors there is
currently a restriction that you may only subclass from a Java class one level deep. You may not
subclass from a Fantom class which itself subclasses from a Java class.
Another restriction: because a Fantom class may not override overloaded methods, you may not
create a concrete Fantom class which inherits abstract overloaded methods.

Overrides
When a Fantom type subclasses a Java type, you can override the Java methods using the normal
Fantom syntax with the following restrictions:
• cannot override static or final methods
• cannot override a method overloaded by field of same name
• cannot override a method overloaded by parameter types
• cannot override a method which uses multi-dimensional arrays in its signature
Consider this example:
// Java class
class Java
{
void alpha() {}
void alpha(Java x) {}
void beta(Java x) {}
int gamma(String[] x) {}
}

In the example above, the method alpha is overloaded by parameter types, therefore it is not
permissible for a Fantom class to override it. However we can override beta and gamma:
class Fantom : Java
{
override Void beta(Java? x) {}
override Int gamma(Str?[]? x) {}
}

When we override a Java method we use the Fantom type representation of the signature. In
thegamma method we map the return type int to sys::Int and the argument type from String[] to
the Fantom list Str[].

Inner Classes
Inner classes in Java source are formatted using a dot, but in the Java VM they are represented
using the "$" dollar sign. We import inner classes into the Fantom type system using the Java VM
name:
using [java] java.util::Map$Entry as Entry

Note that since the "$" is not a legal identifier char in Fantom, you must rename the Java inner
class to a valid Fantom identifier with the as keyword.
Dynamic Calls
Normal Fantom reflection and dynamic invoke is available with Java classes. The Fantom runtime
will correctly map reflective calls and dynamic invokes against overloaded methods. Because it is
possible for a Java class to have both a field and method of the same
name, Type.field andType.method might return different results for the same name. If an attempt is
made to use dynamic invoke on slot which has both a field and method of that name, then the
method always hides the field.

Functions as Interfaces
Inner classes are often used in Java as a substitute for closures. The Java FFI allows you to use a
function where an interface with one method is expected. For example the
interface Runnabledefines one method called run, so we can use a Fantom function whenever
a Runnable is expected:
// Java API
void execute(Runnable r)

// Fantom code
execute |->| { echo("run!") }

The standard rules for coercion between Java and Fantom types apply for how the function
implements the interface's method.

Annotations
Java annotations are imported into the Fantom type system as a special case of facets. Fantom
classes and slots may be annotated with a Java annotation using the standard facet syntax:
// Java annotations
public @interface AnnoA {}
public @interface AnnoB { String value(); }
public @interface AnnoC { int i(); String s(); }

// Fantom syntax
@AnnoA
@AnnoB { value = "foo" }
@AnnoC { i = 34; s = "bar" }
class Fantom {}

Assuming the annotation has a runtime retention policy, annotations added to a Fantom type or
slot are available for Java reflection. However, the annotation will not be reflected in the Fantom
type system as a facet.
The following annotation element types are currently supported:
Java Fantom
---- -------
boolean sys::Bool literal
byte, short, int, long sys::Int literal
float, double sys::Float literal
String sys::Str literal
Class sys::Type literal
enum Java FFI enum field access
arrays of above sys::List literal of above
Dotnet FFI
Overview
Unfortunately the .NET foreign function interface is not available yet. If you need it please let us
know!
Natives
Overview
Native classes, methods, and fields are used to implement Fantom in the native language of the
runtime platform. For example a native method would be implemented in Java for the JVM, in C#
for the CLR, and JavaScript for browsers. Natives are the glue code for portable libraries such
asinet and fwt.
Here is a quick check-list for when and when not to write native code:
1. If possible then the best solution is to write your code in 100% Fantom; then it is portable
across any platform
2. If you don't care about portability, then use a FFI to call out to a native library -
see JavaFFIand DotnetFFI.
3. If you need to call out native APIs, but still wish your Fantom pod to be portable across
multiple platforms then use natives
When writing native code, refer to the FFI documentation for how to map Fantom types to their
native platform types. For example see Java FFI to map between Fantom and Java types.

Native Classes
A native class is one where the entire implementation is coded up in the native language. In
general the native code must look exactly like what the compiler/runtime would emit. The sys pod
is implemented entirely as native classes which makes it a great place to look for examples in
Java, C#, and JavaScript.
A native class is indicated with the native keyword in the class header:
native class Foo
{
new make(Str arg) {}
Int add(Int a, Int b)
Str? a
const Str b := "const"
}

All methods are assumed to be native and must not have a body. There must be an
implementation class for each platform. Here is what the Java implementation would look like:
class Foo extends FanObj
{
// constructor factory called by Foo.make
public static Foo make(String arg)
{
Foo self = new Foo();
make$(self, arg);
return self;
}

// constructor implementation called by subclasses


public static void make$(Foo self, Str arg) {}

// boiler plate for reflection


public Type typeof()
{
if (type == null) type = Type.find("mypod::Foo");
return type;
}
private static Type type;
// methods
public long add(long a, long b) { return a + b; }

// mutable field
public String a() { return a; }
public void a(String it) { a = it; }
private String a;

// const field
public String b = "const";
}

Native Peers
The general design for classes with native methods and fields is to create a peer class for each
Fantom type. These peers may be a singleton shared by all Fantom instances or you may use a
peer instance per Fantom instance. Note that peers are not used with native classes.
Any class which defines a native slot must declare a peer class:
// Fantom code
class Foo
{
native Int add(Int a, Int b)
}

// Java peer
package fan.mypod;
public class FooPeer
{
public static FooPeer make(Foo self) { return new FooPeer(); }
public long add(Foo self, long a, long b) { return a + b; }
}

// C# peer
namespace Fan.Mypod
{
public class FooPeer
{
public static FooPeer make(Foo self) { return new FooPeer(); }
public long add(Foo self, long a, long b) { return a + b; }
}
}

// JavaScript peer
fan.mypod.FooPeer = fan.sys.Obj.$extend(fan.sys.Obj);
fan.mypod.FooPeer.prototype.$ctor = function(self) {}
fan.mypod.FooPeer.prototype.add = function(self, a, b) { return a + b; }

The peer is always accessible from the Fantom instance via a built-in field called peer. When
creating class hieararchies with natives, it is up your peer factory to override the peer fields of
super classes:
public static FooPeer make(Foo t)
{
FooPeer peer = new FooPeer();
((FooBaseClass)t).peer = peer; // override base class's peer field
return peer;
}
Native Methods
Native methods are always routed to the peer:
// Fantom
class Foo
{
native Str a(Bool x)
static native Void b(Int x)
}

// Java or C#
class FooPeer
{
public static FooPeer make(Foo self) { return new FooPeer(); }

// instance methods always take implicit self


public String a(Foo self, boolean x) { return "a"; }

// static methods are just normal statics with matching signatures


public static void b(long x) {}
}

// JavaScript
fan.mypod.FooPeer.prototype.a = function(self, x) { return "a"; }
fan.mypod.FooPeer.b = function(x) {}

All non-static methods and fields will pass the Fantom instance as an implicit first argument. This
lets you use a singleton peer for all instances. Typically you will only allocate a peer instance if you
wish to manage state on the peer.

Native Fields
Native fields are similar to abstract fields in that they generate a getter and setter, but no actual
storage. The emit process will route the Fantom getter/setter to the peer class:
// Fantom
class Foo
{
native Str? f
}

// Java
class FooPeer
{
public static FooPeer make(Foo self) { return new FooPeer(); }
public String f(Foo t) { return f; }
public void f(Foo t, String v) { f = v; }
String f;
}

// C#
class FooPeer
{
public static FooPeer make(Foo self) { return new FooPeer(); }
public string f(Foo t) { return m_f; }
public void f(Foo t, String v) { m_f = v; }
string m_f;
}

// JavaScript
fan.mypod.FooPeer.prototype.m_f = "";
fan.mypod.FooPeer.prototype.f = function(t) { return this.m_f; }
fan.mypod.FooPeer.prototype.f$ = function(t, v) { this.m_f = v; }
Native fields can be virtual or override a superclass, but cannot be const, static, or abstract.
JavaScript
Overview
Fantom provides support for compiling to JavaScript and running in JavaScript VMs such as web
browsers. Most of the sys API is available, however not all pods and APIs are accessible due to
limitations of the JavaScript VM enviornment.

Js Facet
You must explictly mark types you intend to compile to JavaScript using the sys::Js facet:
@Js
class GonnaBeJs
{
Void sayHi() { Win.cur.alert("Hello!") }
}

Natives
To compile JavaScript natives, add the source directories to your build script using the jsDirsfield.
See Build Pod for an example.
Deployment
Overview
The Fantom compiler generates pod files which contain a bytecode representation of the code
which we call fcode. Pod files are just normal zip files you can open in your favorite zip tool. Pod
files also contain files for the constant pools and any resource files you might have bundled with
your pod (available via the Pod.files method).
Pod files are portable between Java and .NET. The runtimes are responsible for reading in pod
files to execute them. The runtimes also provide a full implementation of the sys pod:
• The Java runtime reads pods files and emits the fcode as Java bytecode. This translation
occurs at runtime. The sys APIs are implemented in normal Java code.
• Likewise the .NET runtime reads pods files and emits the fcode as IL at runtime. The sys
APIs are implemented in normal C# code.
The following illustration depicts this architecture:

Natives
If a pod is written 100% in Fantom code, then it is completely portable between the runtimes.
However some pods like inet or fwt need to bind the underlying platform with native methods.
When you build a pod with native methods, it generates a normal pod file which is necessary for all
the reflective metadata. But it also generates a native file. For Java natives this is a jar file under
the "lib/java" directory. For .NET natives a dll file is generated under "lib/dotnet". Also
seedocTools for how to build pods with native code.
Note: natives have been obseleted by the Java FFI.

Dependencies
All pods have an explicit set of dependencies on other pods. All pods must have a dependency on
the sys pod. Dependencies are declared in your build script and are available at runtime
viaPod.depends.
Dependencies are modeled via the sys::Depend class. They are declared in a string format which
includes the pod name and a set of version constraints. Version constraints can be a simple
version number, a version number and anything greater, or a version range. See
Depend's fandocfor the format details.
Dependencies are used in two ways. At compile time dependencies determine which pods can be
imported via the using statement. It is a compile time error to import a pod which isn't declared in
the dependency list.
Dependencies are also checked at runtime. If a pod's dependencies are not met, then the pod
cannot be loaded.

Application Deployment
Deploying a Fantom application involves three components:
1. Platform runtime: either the Java VM or the .NET VM (usually pre-installed)
2. Fantom runtime: a distribution of the core files
3. Pods: the library of pods necessary for your application
The Fantom distribution downloaded from the web is really a developer distro. Most of those files
are not needed for runtime. In general the only directories needed for runtime are bin, lib, andetc.
Within the lib directory you can remove all the pod, jar, and dll files not needed by your
application.
Env
Overview
The sys::Env class is used to access and customize the Fantom environment. Key aspects of the
runtime delegate to the currently installed environment including:
• standard I/O
• home, working, and temporary directories
• pod resolution
• config property resolution
• locale property resolution
• Java classloading resolution (when using JVM)

Directory Structure
The standard directory structure for Fantom installations is:
lib/
fan/
podA.pod
podB.pod
.. other pods
java/
.. jar files
dotnet/
.. .NET DLLs
etc/
sys/
config.props
... other sys config/runtime files
build/
config.props
... other build config/runtime files
podA/
config.props
... other config/runtime files
podB/
config.props
... other config/runtime files

The top level "lib" directory is used to store library files which contain code. The directories under
"lib" are organized by platform: fan, java, dotnet. The ".pod" files themselves are stored under
"lib/fan/".
The top level "etc" directory is used to store system wide configuration files and other files that a
pod might need at runtime. The directories under "etc" are organized by pod - each subdirectory
under "etc" should match a pod name.

BootEnv
The default envirnoment used to boots up a Fantom environment is called the BootEnv.
ThehomeDir of the BootEnv is always relative to the location used to launch Fantom (fan.exe or fan
bash script).
Core libraries like sys must be loaded out of the BootEnv.
Setting Env
You can override the default environment using the FAN_ENV environmental variable. This variable
should map to a qualified type name of the Env subclass to use. The class must have a default no-
argument constructor.
The BootEnv is used to load the environment class specified. This requires that the pod defining
your environment is located under "{homeDir}/lib/fan/".

PathEnv
The util::PathEnv is a simple env implementation that uses a search path instead of requiring all
your files be located under the "fan.home". The search path is specified with
the FAN_ENV_PATHenvirnomenal variable. For example:
C:\dev\fan>set FAN_ENV=util::PathEnv
C:\dev\fan>set FAN_ENV_PATH=/dev/work/

The directory specified should be structured using the standard conventions. In the example above
we would look for pods in "/dev/work/lib/fan/". You can specify more than one directory using your
operating system's path separator. Note that the path always implicitly includes "fan.home" as the
last directory in search path.
The list of paths is specified in priority order. The first directory in the path is used for workDir.
Priority order is used to resolve pods and configuration files. Typically you will use this mechanism
to keep a pristine boot directory, and do pod development and configuration overrides in a
separate working directory.
For example let's consider this directory structure:
boot/
lib/
fan/
podA.pod
podB.pod
work/
lib/
fan/
podB.pod
podC.pod

In this configuration, things would be resolved as follows:


podA => boot/lib/fan/podA.pod
podB => work/lib/fan/podB.pod
podC => work/lib/fan/podC.pod

Note how work directory trumps boot for resolution of podB even though it exists in both
directories.

JarDistEnv
The JarDistEnv is a JVM only environment which is used to deploy a set of Fantom pods as a
single Java JAR file. A JarDistEnv differs from other environments in that it has no access to the
standard directory structure on the file system. Rather everything the environment might need is
self contained as resources inside the JAR file.
The following features are not supported in the JarDistEnv:
• indexed props
• file system overrides for config and locale
• homeDir, workDir, tempDir all default to current working directory
See the build::JarDist task and example script for how to build a JAR for deployment.
In order for the Fantom to boot itself from a JarDist, the following system properties must be
configured:
• "fan.home": set to some directory for sys::Env.homeDir
• "fan.jardist": set to "true" to ensure runtime loads itself from the JAR instead of the file
system

Indexed Props
One issue which plagues many software platforms is the ability to efficiently discovery what is
currently installed. For example what type should I use to handle the URI scheme "foobar"? What
plugins are registered to work with the "image/png" MIME type?
In Fantom building these types of discovery functions is done with indexed props. Indexed props
are a simple mechanism where pods can define name/value pairs which are coalesced into a
master index by the current environment. You define indexed props in your pod's build script:
index =
[
// creating plugins for existing APIs like UriScheme
"sys.uriScheme.foobar": "acme::FoobarScheme",

// creating plugins for specific target types


"acme.editor.sys::Bool": "acme::BoolEditor",
"acme.editor.sys::Int": "acme::IntEditor",

// registering types for specific functions


"acme.theme": ["acme::JungleTheme", "acme::WaterTheme"],
]

Indexed props are globally scope, so convention is to scope your key names with a pod name.
Note in the above example "acme.theme" that you can define multiple values for a single key.
During runtime the current environment will build master index of indexed props for all the installed
pods. This means adding new functionality requires only to drop a pod file into your lib directory.
You lookup index props with the Env.index method. It returns a list of values bound to a given key:
// lookup the UriScheme bound to foobar
qname := Env.cur.index("sys.uriScheme.foobar").first

// find editor types responsible for editing target


qnames := Env.cur.index("acme.editor.${target.typeof}")

// find all the theme types installed


qnames := Env.cur.index("acme.theme")

Using the basic mechanisms of name/value pairs, you can construct fairly sophisticated solutions
for discovering the types and resources bundled in the installed pods.
Serialization
Overview
Serialization is the process of writing objects to an output stream, and reading them back from an
input stream. Serialization provides a simple mechanism to persist objects to a file or to pass
objects over a network. Serialization is also used with actors as a safe way to pass messages
between actors. Fantom serialization uses a human friendly text format which looks a lot just like
Fantom source code (in fact it is a subset of the source grammar).

Data Model
Serialization in Java is graph based - it will handle an arbitrary number of references to a particular
object. Fantom serialization is strictly tree based, it will not attempt to keep track of object
references - it is up to you design your data models as a tree. If you need to cross reference
objects in your tree, then you should use a Uri or some other identifier.
Each object in the tree is classified as a literal, simple, or complex. Most of the standard Fantom
literals such as Bool, Int, Str are supported as well as the collections types List and Map. Simples
are leaf nodes serialized via a string representation. Complexes are an aggregate node defined by
their fields which store nested objects (either literals, simples, or other complexes). You can also
mark any complex type as a collection.

Serializable
The sys::Serializable facet is used to mark types which are serializable. By default a serializable
object will serialize all of its non-static fields. You can use the sys::Transient facet to annotate a
field which should not be serialized. A contrived example:
@Serializable
class Rectangle
{
Int x; Int y
Int w; Int h
@Transient Int area
}

A serializable object must support a make constructor which either takes no parameters or takes an
it-block. If the constructor takes an it-block then the field values are passed in
usingField.makeSetFunc and the object is given a chance to perform validation. For example:
@Serializable
const class Rec
{
new make(|This| f)
{
f(this)
if (id <= 0) throw ArgErr("id <= 0")
}
const Int id
}

"Rec { id = 3 }".in.readObj // ok
"Rec {}".in.readObj // throws ArgErr
Simple
Set the Serializable.simple field to mark a type which is serialized atomically to and from a string
representation. The sys::Obj.toStr method must return a suitable string representation of the
object. Each simple type must also declare a static method called fromStr which takes one or more
parameters where the first parameter is a Str and returns an instance of the object. An example:
@Serializable { simple = true }
class Point
{
static Point fromStr(Str s) { t := s.split(','); return make(t[0].toInt, t[1].toInt) }
new make(Int x, Int y) { this.x = x; this.y = y }
override Str toStr() { return "$x,$y" }
Int x := 0
Int y := 0
}

Collection
Set the Serializable.collection field to mark a type as a container of child objects. Collections
provide a concise syntax for nesting zero or more children items in the same scope as any
serialized fields. This allows you to nest configuration items and children inside one set of curly
braces. Every collection type must support an add and each method which are used
by readObjand writeObj respectively. See the example code.

Streams
The following methods are available for serialization:
• InStream.readObj:read single object
• OutStream.writeObj: write single object

Serializing objects to and from streams is a piece of cake:


// write an object to an output stream
out.writeObj(obj)

// read an object from an input stream


obj := in.readObj

Both Buf and File have convenience methods. For example to serialize to and from a file:
obj := [true, 5, "hi", `file.txt`]
f := File(`test.txt`)
f.writeObj(obj)
obj2 := f.readObj

By default writeObj will optimize for performance. But if you are generating a file which should look
pretty for humans to read and edit, you can control the output using options. For example to indent
each level of the output tree by 2 spaces and skip fields at their default values:
out.writeObj(obj, ["indent":2, "skipDefaults":true])

Syntax
The Fantom serialization syntax is designed to be easy to read and write by a human, but also
efficient for machine processing. The syntax is based on the Fantom programming language itself,
although it is purely declarative (no expressions or behavior). An object is defined as one of:
• Literal: one of the standard Fantom literals using the exact same representation as you
would use in your source code (this includes List and Map)
• Simple: the string representation of a simple type
• Complex: a type and its list of field name/values pairs
The Fantom programming language is a complete superset of the serialization syntax - you can
paste any serialized object into a source file as an expression.

Using
You can include zero or more using statements at the top of a serialized object document. Using
statements allow unqualified type names to be used:
// qualified names
["red":fwt::Color("#f00"), "blue":fwt::Color("#0f0")]

// unqualified names
using fwt
["red":Color("#f00"), "blue":Color("#0f0")]

You can use any of the standard using statements in your serialization documents:
using pod => import all types in pod
using pod::name => import single type
using pod::name as rename => import type with new name

Note that unlike normal source code, the sys pod is not imported automatically. If you wish to use
unqualified type names for the sys pod, then you need to explicitly import via using sys.

Literals
Most of the standard Fantom literals are serialized using the same representation as defined by the
Fantom programming language:
• sys::Bool
• sys::Int
• sys::Float
• sys::Decimal
• sys::Str
• sys::Duration
• sys::Uri
• sys::Type
• sys::Slot
• sys::List
• sys::Map
NOTE: the special Float values NaN, INF, and -INF must be represented using the simple syntax:
sys::Float("NaN") sys::Float("INF") sys::Float("-INF")

Simples
A simple is serialized as: <type>("<toStr>"). When writing the object, the Obj.toStr method is
called to obtain the string representation. When reading the object the static fromStr method is
used to decode the string back into an object. Examples:
sys::Version("1.2")
sys::Depend("foo 1.2-3.4")

You may use this syntax directly in source code via the simple expression.
Complex
A complex is serialized as a list of field name/value pairs separated by a newline or a semicolon
(just like Fantom statements). Any field can be omitted, in which case the field's default is used.
The syntax for a complex is:
<type>
{
<field1> = <value1>
<field2> = <value2>
...
}

An example of a serializable class and an serialized instance:


@Serializable
class Person
{
Str name
Int age
Str[] children
Str address
}

acme::Person
{
name = "Homer Simson"
age = 39
children = ["Bart", "Lisa", "Maggie"]
}

You may use this syntax directly in source code via it-blocks.

Collection
Collections are serialized just like a complex - all the fields are serialized as name/value pairs. After
the fields are serialized, all the child items iterated by the each method are serialized with a comma
separator. During deserialization, the children are added back via the add method.
We can rewrite the Person example above as a collection:
@Serializable { collection = true }
class Person
{
Void add(Person kid) { kids.add(kid) }
Void each(|Person kid| f) { kids.each(f) }
Str name
@transient private Person[] kids := Person[,]
}

acme::Person
{
name = "Homer Simson"
acme::Person { name = "Bart" },
acme::Person { name = "Lisa" },
acme::Person { name = "Maggie" },
}

This syntax is also supported directly in source code via it-blocks.

Grammar
The formal grammar of the Fantom serialization formats:
// .fog file format for single object
objDoc := header obj

header := [using]*
using := usingPod | usingType | usingAs
usingPod := "using" id eos
usingType := "using" id "::" id eos
usingAs := "using" id "::" id "as" id eos
obj := literal | simple | complex
literal := bool | int | float | decimal | str | duration | uri |
typeLiteral | slotLiteral | list | map
simple := type "(" str ")"
complex := type ["{" [children] "}"]
children := child [(eos|",") child]*
child := field | item
eos := ";" | newline
field := id "=" obj
item := obj

The literal, using, and type productions use the same grammar as the Fantom programming
language. However the type production can never be a function type.
Concurrency
Overview
Fantom tackles concurrency using a very different path from Java and C#. Java and C# use a
shared memory model - all threads have access to each other's memory space. Synchronization
locks are required to ensure that threads share data in a consistent manner. This concurrency
model is quite powerful, but operates at a low level of abstraction. As such, even skilled
programmers have a hard time writing code which is free of both race conditions and deadlocks.
This model also makes it very hard to create composable systems because all system components
must orchestrate their use of locking consistently.
The Fantom model of concurrency is based upon the following principles:
• No Shared Mutable State: threads never share mutable state under any circumstances;
• Immutability: the notion of immutability is embedded into the language itself. Immutable
data can be efficiently and safely shared between threads (for example via a static field);
• Message Passing: the actor API is built around the idea of passing messages between
asynchronous workers;

Immutability
An object is said to be immutable if we can guarantee that once constructed it never changes
state. Fantom supports these types of immutable objects:
• Any object instance of a const class
• The result of Obj.toImmutable on a List, Map, or Func
By definition a const class is immutable - the compiler verifies that all the instance fields are
themselves immutable and only set in the object's constructor.
Func objects are determined as mutable or immutable by the compiler depending on if the function
captures mutable state from its environment. See Functions for more details.
The toImmutable method supported by List and Map is a mechanism to return a readonly, deep
copy of the collection which ensures all that all values and keys are themselves immutable. The
compiler will allow assignment to const List/Map fields during construction, but it implicitly makes a
call to toImmutable. For example to declare a const list of strings:
// what you write
class SouthPark
{
const static Str[] names := ["Stan", "Cartman", "Kenny"]
}

// what the compiler generates


class SouthPark
{
const static Str[] names := ["Stan", "Cartman", "Kenny"]?.toImmutable
}

You can check if an object is immutable via the Obj.isImmutable method.

Shared Objects
Actors allow objects to be shared between threads. Specific APIs which will pass an object to
another thread include:
• Actor.send
• Actor.sendLater
• Actor.sendWhenDone
• Future.get

All of these APIs use the same pattern to safely pass an object between threads. If an object is
immutable then we can safely pass it to another thread by reference. Otherwise we assume the
object is serializable and pass a serialized copy of the object to another thread. Both of these
approaches have their pros and cons to consider in your application design:
• immutable:
• ideal for simple structures if fast to create
• deeply structured objects expensive to change inside a single thread
• always extremely efficient to pass between threads
• serializable:
• deeply structured objects can be modified efficiently in a single thread
• moderately expensive to pass between threads
Actors
Overview
Fantom includes an actor framework for concurrency. Actors are light weight objects which
asynchronously process work on a background thread. Actors are given work by sending them
asynchronous message. Actor's then process those messages on background threads controlled
by an ActorPool.

Actors
The Actor class is used to define new actors. All actors are constructed within an ActorPoolwhich
defines how the actor is executed.
Actors may define their receive behavior in one of two ways:
1. Pass a function to the Actor's constructor
2. Subclass Actor and override receive
Here are two simple examples of an actor which receives an Int message and returns the
increment:
// pass receive to constructor as a closure function
a := Actor(pool) |Int msg->Int| { msg + 1 }

// subclass and override receive


const class IncrActor : Actor
{
new make(ActorPool p) : super(p) {}
override Obj? receive(Obj? msg) { msg->increment }
}

An actor is guaranteed to receive its messages atomically - it is never scheduled on multiple


threads concurrently. However, an actor is not guaranteed to receive all of its messages on the
same thread over time. For example if messages A, B, and C are sent to an actor, the runtime may
use three different threads to process those messages. But the actor is guaranteed to process the
messages serially one after the other.

Actor Locals
Actors are const classes which means they must be immutable. This lets you pass actor
references between actors, but you can't maintain any mutable state in the actor's fields. Instead
you can store the actor's "mutable world state" in Actor.locals. Actor locals is a string/object map
which works like a thread local - a unique map is used for every actor. To prevent naming
collisions, you should prefix your map keys with your pod name:
// store a actor local
Actor.locals["acme.global"] = "hum bug"

// get a thread local


Actor.locals["acme.global"]

For example to build an actor which maintains a counter every time it receives a message:
pool := ActorPool()
a := Actor(pool) |msg|
{
count := 1 + Actor.locals.get("count", 0)
Actor.locals["count"] = count
return count
}
100.times { a.send("ignored") }
echo("Count is now " + a.send("ignored").get)

Note that in this example, the actor ignores the messages sent to it, so it doesn't really matter what
we pass.

Message Passing
Actors communicate by sending each other messages. Messages cannot be used to pass mutable
state between actors. If a message is immutable then it passed by reference. Otherwise the
message must be serializable in which case a serialized copy of the object is passed. If a message
is neither immutable or serializable, then IOErr is thrown.
Messages are sent to an actor using of three methods:
• send:enqueues the message immediately
• sendLater: enqueues the message after a period of time has elapsed
• sendWhenDone: enqueues the message once another message completes processing

Futures
All three send methods return a Future which may used to access the result of that message. You
can poll for the result using isDone - a future enters the done state by one of three transitions:
• The actor processes the message and returns a result
• The actor raises an exception while processing the message
• The future is cancelled (see cancel)
Once a future enters the done state, the result is available via the get method. If the future is not
done, then calling get will block the caller until the future becomes done. A timeout may be used to
block for a fixed period of time. Calling get results in one of these outcomes:
• If the message was processed successfully, then get will return the result
• If the actor raised an exception processing the message, then that same exception is raised
to the caller of get
• If the future was cancelled, then calling get will raise CancelledErr
• If a timeout is used, then TimeoutErr is thrown if the actor doesn't process the message
before the timeout elapses
Actors which block via Future.get should never receive messages themselves as this might lead
to deadlocks. Best practice is to design service actors using strictly asynchronous messaging, and
keep synchronous messaging on client actors which don't service requests themselves.

Timers
The sendLater method can be used to setup a timer. Timers post a message back to the actor's
queue when they expire. Example:
pool := ActorPool()
a := Actor(pool) |Obj msg| { echo("$Time.now: $msg") }
a.send("start")
a.sendLater(1sec, "1sec")
a.sendLater(3sec, "3sec")
a.sendLater(2sec, "2sec")
Actor.sleep(5sec)

The sendLater method returns a Future which may be used to cancel the timer or poll/block until
the message has been processed.
Chaining
The sendWhenDone method is used to deliver a message once another message has completed
processing. Using sendWhenDone allows asynchronous message chaining. Consider this code:
future := actor1.send(msg1)
actor2.sendWhenDone(future, msg2)

In this example, msg2 is enqueued on actor2 only after actor1 completes processing of msg1.
Typically the future itself is passed as the message:
a.sendWhenDone(future, future) // future is message itself
a.sendWhenDone(future, MyMsg(future)) // MyMsg references future

Remember that sendWhenDone is called no matter how the future completes: successfully, with an
error, or cancellation.

Coalescing Messages
Often when sending messages to an actor, we can merge two messages into a single message to
save ourselves some work. For example, it is common in windowing systems to maintain a single
union of all the dirty portions of a window rather than of a bunch of little rectangles. An actor can
have its messages automatically coalesced using the makeCoalescing constructor.
Let's look at an example:
const class Repaint
{
new make(Window w, Rect d) { ... }
Window window
Rect dirty
}

toKey := |Repaint msg->Obj| { msg.window }


coalesce := |Repaint a, Repaint b->Obj| { Repaint(a.window, a.dirty.union(b.dirty)) }
a := Actor.makeCoalescing(g, toKey, coalesce) |Repaint msg| {...}

In this example the messages are instances of Repaint. The toKey function is used to obtain the
key which determines if two messages can be coalesced. In this example we coalesce repaints per
window. If the thread detects two pending messages with the same key (the window in this case),
then it calls the coalesce function to merge the messages. In example we return a
newRepaint event with the union of the two dirty regions.
Messages sent with sendLater and sendWhenDone are never coalsesed.

Flow Control
The current implementation of Fantom uses unbounded message queues. This means if an actor
is receiving messages faster than it can process them, then its queue will continue to grow.
Eventually this might result in out of memory exceptions. You can use some of the following
techniques to implement flow control to prevent unbounded queues from growing forever:
• Poll futures with isDone or use get with timeouts to cancel messages which aren't
processed after a period of time
• Use coalescing queues to merge pending messages
• Use sendLater to schedule watch dog timers on an actor's queue
• Use sendWhenDone to create message feedback loops
For example consider a "reader" actor which reads lines of text from a big text file and sends those
lines to other "processing" actors for parallel processing. If the reader pushes the lines of text as
fast as it can read them, then it could potentially end up enqueuing large numbers of lines in
memory. A better strategy would be to have the processing actors enqueue themselves with the
reader when they are ready to process a line. This would create a natural feedback loop and allow
the reader to throttle its IO based on how fast the processors could work.

Actor Pools
All actor's are created within an ActorPool. ActorPools manage the execution of actors using a
shared thread pool.
As messages are sent to actors, they are allocated a thread to perform their work. An ActorPool will
create up to 100 threads, after which actor's must wait for a thread to free up. Once a thread frees
up, then it is used to process the next actor. If no actor's have pending work, then the thread
lingers for a few seconds before being released back to the operating system. In this model an
ActorPool utilizes between zero and a peak of 100 threads depending on how many of the pool's
actors currently have work. You can tweak the peak limit by setting Actor.maxThreads:
ActorPool { maxThreads = 10 }

An ActorPool is immediately considered running as soon as it is constructed. However, it doesn't


actually spawn its first thread until one of its actors is sent a message. If all of a pool's actors finish
processing their messages, then after a linger period all of that pool's threads be freed.
An ActorPool can be manually shutdown using the stop method. Once stop is called, the pool
enters the stopped state and actors within the pool may not receive any more messages. However
all pending messages are allowed to continue processing. Once all pending messages have been
processed, the pool enters the done state. Use the join method to block until an ActorPool has
fully shutdown.
The kill method can be used to perform an unorderly shutdown. Unlike stop, kill doesn't give
actors a chance to finish processing their pending message queues - all pending messages are
cancelled. Actors which are currently executing a message are interrupted (which may or may not
immediately terminate that thread). Once all actors have relinquished their threads, the ActorPool
enters the done state.
Naming
Overview
A "name" is a set of conventions and rules for using strings as identifiers. Good names are typically
human readable, although that isn't necessarily required. Fantom provides a unified naming design
based on the sys::Uri class. Uris are a good choice for naming because they provide:
• Ability to transcribe the name into an ASCII string (with appropriate escaping)
• Well defined model for plugging in "protocol" (scheme) handlers
• Well defined model for path hierarchies
• Well defined model for name/value pairs via queries
• Well defined rules for relativization and normalization
• Uris map cleanly to web based applications
• Uris are widely supported in alternate languages and platforms

Resolving Uris
In Fantom anything of interest we might wish to identify with a name is assigned a Uri. We resolve
Uris to sys::Obj instances. The actual object type is dependent on the Uri. For example all "file:"
Uris will resolve to a sys::File. Resolving a Uri is done via the Uri.get method:
File f := `file:/dir/file.txt`.get

If the file cannot be resolved, then UnresolvedErr is thrown. You can pass false for
the checkedparameter to return null if the Uri cannot be resolved.
The default behavior of Uri.get on a relative Uri (null scheme) is to throw UnresolvedErr. But you
can pass in optional base object. If the Uri is relative, then we attempt to resolve the base object's
uri via the dynamic call base->uri. If the base's uri is absolute, then resolve we base->uri + uri:
base := `file:/dir/`.get
`file.txt`.get(base) => resolves to `file:/dir/file.txt`

Uri Schemes
The sys::UriScheme class is used to plug in handling for new Uri schemes. The standard fan
runtime provides support for the following schemes:
• fan: resolves to the objects in the Fantom namespace (discussed below)
• file: resolves to File instances on the local file system
• http: not done yet, but coming soon...
• flux: application specific uris for flux (like "about:" in Firefox)
You can plug in your own scheme handling by subclassing UriScheme:
const class CustomScheme : UriScheme
{
override Obj? get(Uri uri, Obj? base) { ... }
}

You override the get method to implement uri to object resolution. To register your scheme, define
an indexed prop formatted as:
sys.uriScheme.{scheme}={qname}

Where scheme is the lower case scheme name and qname is the qualified name of your scheme
type. Indexed props are defined your build script, for example:
index = ["sys.uriScheme.fan": "sys::FanScheme"]

Fan Scheme
The Fantom runtime includes support for the "fan:" scheme which is used to identify objects related
to the Fantom namespace:
fan://pod => sys::Pod
fan://pod/Type => sys::Type
fan://pod/Type#slot => sys::Slot (Field or Method)
fan://pod/dir/file.txt => sys:Pod.file (resource file)
Logging
Log Creation
The sys::Log class standardizes how to embed logging statements into Fantom applications.
Every Log instance in the VM has a unique name which by convention always starts with the pod
name and uses dot separators. Once a Log instance has been created for a specified name, it
remains bound to that name for the lifetime of the VM. Logs are const, immutable instances shared
by all threads.
Most of the time, you should just use Pod.log to get the standard log for your pod:
Pod.of(this).log.err("...")

You can also use Log.get which will create the Log on the first call, and look it up on subsequent
calls:
// get or create a log named "acme"
const static Log log = Log.get("acme")

// find an existing log


Log.find("acme") // throw exception if not found
Log.find("acme", false) // return null if not found

// list all the active logs


Log.list

Log Statements
The following methods are used to generate log records:
• Log.err:something bad happened
• Log.warn: something happened which might be bad
• Log.info: something interesting happened
• Log.debug: something happened which is interesting only if you happen to be debugging

All logging methods take a Str message, and an optional Err. Some simple examples:
log.err("The freaking file didn't load", err)
log.info("CatchRoadRoader service started on port $port")

When writing debug log statements, we expect that they will be turned off most of the time.
Therefore be aware of the hidden costs of string concatenation. You can use the isDebug method
to skip creating a log message:
// this code performs string concatenation on every call
log.debug("The values are x=$x, y=$y, and z=$z")

// this code performs string concatenation only when needed


if (log.isDebug)
log.debug("The values are x=$x, y=$y, and z=$z")

Log Levels
Each Log is configured to log events at or above a given LogLevel. These levels from least to most
severe:
• debug: log everything
• info: log everything but debug
• warn: log everything but debug, info
• err: log only errors
• silent: log nothing
All logs default to level info (see setup to change default levels).
You can get/set the current severity level of a Log via the level field. Some code examples:
log.level = LogLevel.warn
log.level < LogLevel.err // returns true
log.level < LogLevel.info // returns false

Log Handlers
Log handlers are functions designed to process LogRecs. The following Log methods are used to
manage the handlers in a VM:
• Log.handlers:list the installed handler functions
• Log.addHandler: install a handler function
• Log.removeHandler: uninstall a handler function

Handlers must be an instance of an immutable Func (they are shared by all threads). On startup
there is always one handler installed which will print each record to the console via
theLogRec.print method.
Here is a simple example of an installing a handler:
Log.addHandler |rec| { echo("My Handler: $rec") }

Log Setup
By default all log levels will default to info. You can programatically change the level viaLog.level.
You can also use the "etc/sys/log.props" file to setup the default level for any log. The "log.props"
file is a standard props file where the log name is the key and the value is a StringLogLevel:
web=debug
acmeWombat.requests=silent
acmeWombat.responses=warn
Localization
Locale
The sys::Locale class is the hub API for Fantom's localization infrastructure. Locales are used to
represent a language via an ISO 639 two letter code. A locale may also optionally represent a
specific country or region via a ISO 3166 two letter code. Common locales include:
Language Only:
en English
es Spanish
fr French
de German
it Italian
ja Japanese
ko Korean
zh Chinese
Language and Country:
en-US United States
en-GB United Kingdom
es-ES Spain
es-MX Mexico
fr-FR France
de-DE Germany
en-CA Canada (English)
fr-CA Canada (French)
zh-CN China (Simplified)
zh-TW Taiwan

When the Fantom VM is started it will create a default locale based on the underlying platform. For
example on a Java VM, the default Fantom locale will be initialized from java.util.Locale(which in
turn is initialized from the operating system's default locale).
Locales are configured as an actor local variable. Use sys::Locale.cur and sys::Locale.setCur to
get and set the current thread's locale.
By convention Locale is never passed as a parameter to Fantom APIs. Rather the locale is implied
by the current actor. As a general rule APIs which are locale sensitive will include the termlocale in
their method name.
Use Locale.use to execute a body of code using a different locale:
echo(Locale.cur)
Locale("zh-CN").use { echo(Locale.cur) }
echo(Locale.cur)

Localized Properties
All the strings displayed to users should typically be pulled out into localized props files to enable
easy language translation. Each pod may define one localized property map with as many keys are
desired. Localized properties are defined in props files as pod resource files under
thelocale directory. An example set of localized props files:
locale/en.props
locale/en-US.props
locale/en-CA.props
locale/fr.props
locale/fr-CA.props

The sys::Env.locale API is used to lookup a localized property. Typically you will use
thePod.locale convenience method:
Button(pod.locale("ok"))
Button(pod.locale("cancel"))

Looking up localization is delegated to the current Env. But the standard resolution rules are:
1. Env.props(pod, `locale/{locale}.props`)
2. Env.props(pod, `locale/{lang}.props`)
3. Env.props(pod, `locale/en.props`)
4. Fallback to pod::key unless def specified
Env.props first looks in the pod's etc directory for overrides, then checks if the pod contains a
resource file.
Best practice is to ensure that all properties are mapped in en.props file as your fallback defaults.
Then store localized translations in language files such as fr.props, de.props, etc. Typically you
will only use country specific files such as en-US or en-GB for regional terms.

Locale Literals
String interpolation supports a special mode used to make working with localized props easy. The
following interpolation formats are supported:
• $<key> unqualified key
• $<pod::key> qualified key
• $<key=def> unqualified key with default value

The simplest locale literal is an unqualified key which is just a shortcut for sys::Pod.locale using
the enclosing type:
// original code
class Foo { Void main() { echo("$<bar.baz>") } }

// translated into
class Foo { Void main() { echo(#Foo.pod.locale("bar.baz")) } }

You can also use a qualified key to lookup a localized prop in an external pod:
// original code
"$<fwt::cancel.name>"

// translated into
Pod.find("fwt").locale("cancel.name")

The last format lets you actually define the key's value right in your Fantom source code:
// orginal code
"$<fileNotFound=File not found>: $file"

// translates into
EnclosingType#.pod.locale("fileNotFound", "File not found") + ": $file"

// and automatically adds the key/value pair to locale/en.props


fileNotFound=File not found

If your pod doesn't have an explicit "locale/en.props" resource then it is automatically created. If it
does exist then interpolated key/values are automatically merged into the existing props file. It is a
compile time error to declare a key's value in multiple places; each key must be defined exactly
once in either en.props or in an interpolated string.
DSLs
Overview
DSLs or Domain Specific Languages allow you to embed other languages into your Fantom source
code. The syntax for a DSL is:
AnchorType <|...|>

Everything between the <| and |> tokens is considered source code of the DSL itself. The anchor
type defines how to the compile the DSL. DslPlugins are registered on the anchor type, and called
by the Fantom compiler to translate them into a Fantom expression.

Built-in DSLs
Fantom currently ships with these DSLs:

Str DSL
You can use the Str DSL to write strings which contain any character other than the "|>" sequence.
They work similar to a XML CDATA section or here-documents in languages like Perl, Python, or
Ruby:
echo(Str <|no \ or $ escapes need, and
multi-line works too|>)

See the Str Literals for more details.

Regex DSL
You can use the Regex DSL to construct a sys::Regex instance with a string pattern:
Regex <|foo|foo/(\d*)|>

You don't need to worry about escaping special characters like the backslash.

DslPlugins
You can write your own DSLs by subclassing compiler::DslPlugin. See the RegexDslPlugin class
as a simple example to get started:
@compilerDsl="sys::Regex"
class RegexDslPlugin : DslPlugin
{
new make(Compiler c) : super(c) {}

override Expr compile(DslExpr dsl)


{
regexType := ns.resolveType("sys::Regex")
fromStr := regexType.method("fromStr")
args := [LiteralExpr.makeFor(dsl.location, ns, dsl.src)]
return CallExpr.makeWithMethod(dsl.location, null, fromStr, args)
}
}

Note: writing plugins requires accessing the compiler APIs. This API has a very large surface area,
so we cannot guarantee that these APIs won't change in the future.
Date Time
Overview
The following sys types are used to represent and work with time:
• sys::Duration: period of time measured in nanosecond ticks
• sys::DateTime: an absolute period of time relative to a specific timezone
• sys::TimeZone: represents the rules for UTC offset and daylight savings time according to
zoneinfo database
• sys::Date: a calendar date independent of any time of day
• sys::Time: a time of day independent of any calendar date
• sys::Month: enum for months of the year
• sys::Weekday: enum for days of the week

All these classes are immutable const classes.

Ticks
The basic unit of Fantom time is the nanosecond tick. Both Duration and DateTime have
a ticksmethod and a constructor which takes a number of ticks.
In the case of Durations, ticks are relative. The Duration.now method can be used to track elapsed
time independent of wall-clock time. If you are measuring relative periods of time, you should
always prefer Duration.now because it advances consistently independent of changes to the
system clock. It is not uncommon for computers to automatically adjust their clocks periodically by
several seconds or even minutes which can skew wall-time measurements. In the Java
runtime Duration.now maps to System.nanoTime.
Ticks in DateTime use an epoch of midnight 1-Jan-2000 UTC. Dates before 2000 use negative
ticks. Although the Unix epoch is 1970, we thought since Fantom was born in 2005 we might as
well use the millennium as the epoch.

DateTime
Absolute time is represented as a number of nanosecond ticks relative to the 1-Jan-2000 epoch.
Ticks are a good representation of time for computers, but as humans we think about time as
years, months, days, hours, minutes, etc. The translation from ticks to human time is always
relative to a given timezone. For example 337,350,600,000,000,000 ticks represents 8:30am 9-
Sep-2010 in New York time, but it is 1:30pm in London time. It is the exact same instant in absolute
time, but the human time representation is different based on our timezone perspective.
The DateTime class encapsulates an absolute time in ticks relative to a given timezone. Although
absolute time and timezone human time are two different concepts, it is convenient to bundle them
into a single class. In practice knowing the timezone of a given timestamp is often quite important.
Countless problems are caused when time has an ambiguous timezone association. For example
ISO 8601 time representation provides for an UTC offset, but that is never enough to actually
figure out the timezone. In Fantom we require a unambiguous timezone be associated with every
DateTime and it is part of the canonical serialized representation.
The DateTime class provides nice simple APIs for accessing the human time elements which are
relative to the associated timezone:
d := DateTime.now
echo("$d.day $d.month $d.year | $d.hour $d.min | $d.tz")
echo("$d.date | $d.time")
// outputs on a computer in EDT
9 sep 2010 | 8 58 | New_York
2010-09-09 | 08:58:54.668

Note that the tz method is used to get the associated sys::TimeZone. You can also construct
DateTimes with an explicit timezone or easily convert between timezones:
echo(DateTime.now)
echo(DateTime.nowUtc)
echo(DateTime.now.toTimeZone(TimeZone("Taipei")))

// outputs
2010-09-09T09:00:41.09-04:00 New_York
2010-09-09T13:00:41.106Z UTC
2010-09-09T21:00:41.09+08:00 Taipei

Localization and Formatting


All three classes Date, Time, and DateTime support a toLocale and fromLocale method which can
be used to parse/format using a string pattern. The pattern language is similar to that used by
Java's SimpleDateFormat. But Fantom supports some extra features and adheres to the following
conventions:
• capitalized letters are for date fields (year, month, day, weekday)
• lower case letters are used for time fields (hour, minutes, seconds)
• optional seconds and fractional seconds are capitalized (S and F)
Couple simple examples:
DateTime.now.toLocale("kk:mmaa") => 09:10am
DateTime.now.toLocale("DDD 'of' MMMM, YYYY") => 9th of September, 2010

TimeZone
In Fantom we use the term timezone to encapsulate two concepts: offset from UTC and daylight
saving time rules. For example, US Eastern Standard Time is -5hrs from UTC. But between 2am
on the second Sunday of March and 2am on the first Sunday in November is daylight savings time
(DST) and is -4hrs from UTC.
Because timezones are such a critical aspect of DateTime representation, Fantom requires a
comprehensive model and database of timezones. Timezones are quite problematic for computers
because they are a political abstraction versus a scientific abstraction. This means that a given
region might change its timezone rules (either UTC offset of DST rules) over time. For example, in
2007 the US changed the dates for when DST starts and ends. This means that computing a date
in 2000 uses different rules than 2010 (we call these historical timezones).
Luckily there is a database which keeps track of these rules across regions and time. We use
thezoneinfo database which is used by Unix and some versions of Java. In Fantom we compile a
subset of the zoneinfo database into a binary representation using the "adm/buildtz.fan" script. The
database is stored in "etc/sys". Due to the size of the database, we use random access IO to load
timezones on demand. In JavaScript, the JsTimeZone class is used to define which timezone
definitions are sent to the browser.
The zoneinfo database uses a convention of naming timezones as "Continent/City". For example,
US Eastern time is canonically identified as "America/New_York". Since there are no duplicate city
names between continents, the city name also works as a canonical identifier. Since the timezone
is always included during serialization, we use the city name only as the canonical identifier. In the
API this is distinguished as name and fullName.
Relative TimeZone
In most cases we wish to compare time absolutely. For example if looking at a log file, we would
generally expect to see events from multiple timezones ordered by absolute time. But sometimes
we wish to compare times by their human time. The special timezone "Rel" is used for this
purpose. Any conversion to/from "Rel" preserves the timezone representation and changes the
absolute ticks.
Here is a simple program to demonstrate:
pattern := "DD-MM-YYYY hh:mm zzz"
a := DateTime.fromLocale("01-09-2010 03:00 Los_Angeles", pattern)
b := DateTime.fromLocale("01-09-2010 03:00 Chicago", pattern)
echo("$a ?= $b => ${a==b}")
a = a.toRel
b = b.toRel
echo("$a ?= $b => ${a==b}")

// outputs
2010-09-01T03:00:00-07:00 Los_Angeles ?= 2010-09-01T03:00:00-05:00 Chicago => false
2010-09-01T03:00:00Z Rel ?= 2010-09-01T03:00:00Z Rel => true

Trade-offs
Like any software development, engineering these APIs requires making trade-offs. We hope to
make trade-offs which solve most use cases with a simple, easy-to-use API. But of course its a
matter of perspective based your own personal use cases :-) But it is worthwhile to consider the
trade-offs.
All the Fantom time APIs are based on a nanosecond tick. A 64-bit integer can store between +/-
292 years. So by using nanoseconds as the unit of a tick, we have made a trade-off optimized in
favor of programs which require nanosecond precision versus programs which work with time
spans of 100s years. It is also worth noting that JavaScript treats all numbers as a 64-bit floating
point number, so nanosecond precision is lost when working with large Durations or DateTimes.
Fantom's dates are based on the Gregorian calendar which isn't the only calendar system in use.
But for practical purposes, using the Gregorian system hits 99.9% use case without adding
complications.
The actual UTC time scale uses leap seconds to keep the calendar in sync with solar time. But in
general computer systems don't take leap seconds into account and Fantom doesn't either. From a
practical perspective this makes it easy to convert between Fantom ticks and other representations
such as Java millis.
By building date and time classes into the core sys pod, we don't provide all the hooks and
functionality which might be required for everybody. But this is a trade-off. Our first priority is to
have core representations which all APIs can use without additional dependencies. But we also
feel that the APIs we have are a sweet spot for probably just about every use case. But of course
anybody could create additional APIs in new pods.
Appendix
Type Inference
This section formally defines the rules for how the compiler performs type inference on lists, maps,
and the ternary operator:
// list of expressions
[v1, v2, ...] =>
V = common(v1, v2, ...)

// map of expressions
[k1:v1, k2:v2, ...] =>
K = common(k1, k2, ...)
V = common(v1, v2, ...)

// ternary operator
cond ? t1 : t2 =>
common(t1, t2)

Type inference of collections is based on a function we call common which is used to find the most
common base class among a list of types. The following algorithm is used to compute the common
type:
1. if the list of types is empty return Obj?
2. if the list of types has only one item, return that type
3. if any one type is nullable, then the result is nullable
4. if none of the types is a parameterized generic, then find the most common class which all
the types share; we take only classes into account, mixins are ignored
5. if any one of the types is a parameterized generic then:
1. if all the types are parameterized Lists, then compute the common V type
2. if all the tyeps are parameterized Maps then:
1. if all have the exact same signature, then use that type
2. use sys::Map
3. if all the types are parameterized Funcs then:
1. if all have the exact same signature, then use that type
2. use sys::Func
4. if none of the above holds true, then use Obj
Conventions
Overview
These are the coding conventions we've used in the Fantom code base. By no means are you
required to follow our conventions - they are documented just in case you care. However these
conventions are enforced if contributing code for the core distribution.

Source Files
• Use 7-bit safe ASCII as clean subset of UTF-8
• Use Unix "\n" line endings
• Prefer putting class FooBar in a source file called "FooBar.fan"
• If you have a bunch of little classes, coalesce into a single source
• Separate test classes into separate "test/" directory

Naming
• Type names are upper camel case such as "FooBar"
• Slot names are lower camel case such as "fooBar" (this includes all fields and methods,
even const fields)
• Never use screaming caps such as "FOO_BAR"
• Symbol names are lower camel case
• Pod names are lower camel case and globally unique. You should prefix your pod names
with something to avoid naming conflicts. For example a SourceForge or Google Code
project name is a safe naming convention. Or prefix pods with an organization or domain
name. If you own a ".com" domain, don't include the "com" in your pod names.
• Don't use words which are fully capitalized in your identifiers:
• Use "someId" instead of "someID"
• Use "readXml" instead of "readXML"

Common Names
• Prefer add to append
• Prefer addr to address
• Prefer arg to argument
• Prefer cur to current
• Prefer dotnet/Dotnet
• Prefer err to error
• Prefer html/Html to HTML
• Prefer http/Http to HTTP
• Prefer id/Id to ID or Identifier
• Prefer io/IO to Io
• Prefer loc to location
• Prefer msg to message
• Prefer param to parameter
• Prefer rec to record
• Prefer req to request
• Prefer res to response
• Prefer ro/RO to Ro
• Prefer rw/RW to Rw
• Prefer size to length or count
• Prefer val to value
• Prefer warn to warning
• Prefer xml/Xml to XML

Indention
• Do not use tab characters, use spaces only
• Use two space indention
• Use Allman styling braces:
if (cond)
{
doTrue
}
else
{
doFalse
}

• Prefer a single statement on each line with no semicolon


• Collapse statements onto a single line if they are short and it aids readability
• Leave one space between keyword and opening paren in if, for, while, switch,
and catchstatements

Statements
• Always omit () for method calls with no arguments
• Prefer Foo(...) style constructor with arguments
• Prefer Foo {...} style constructor when using it-block
• Prefer type inference for local variables
• Prefer implicit casting to explicit casting
• Prefer Obj[] to List and Obj:Obj to Map
• Prefer to omit return keyword in single statement methods and closures

Comments
• Use /* */ comments only for commenting out sections of code
• Prefer to use a leading and trailing ** line in a fandoc comment unless the comment is
short:
class Foo
{
**
** This is a very well written comment
**
Void doSomethingCool() {}
}

• Break logical sections up using line of 74 / chars:


//////////////////////////////////////////////////////////////////////////
// Section
//////////////////////////////////////////////////////////////////////////

• Use line of 74 * chars to separate classes in a single source file:


**************************************************************************
** NewClass
**************************************************************************
• We use the following comment at the top of each source file (obviously the names will be
different for you):
//
// Copyright (c) 2008, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 28 Aug 08 Brian Frank Creation
//
Grammar
Legend
Legend for Fantom BNF Grammar:
:= is defined as
<x> non-terminal
"x" literal
[x] optional
(x) grouping
x* zero or more times
x|x or

Compilation Unit
<compilationUnit> := <using*> <typeDef>* [<doc>]
<using> := <usingPod> | <usingType> | <usingAs>
<usingPod> := "using" <podSpec> <eos>
<usingType> := "using" <podSpec> "::" <id> <eos>
<usingAs> := "using" <podSpec> "::" <id> "as" <id> <eos>
<podSpec> := <id> | <str> | <ffiPodSpec>
<ffiPodSpec> := "[" <id> "]" <id> ("." <id>)*

Type Def
<typeDef> := <classDef> | <mixinDef> | <facetDef> | <enumDef>

<classDef> := <typeHeader> "class" <id> [<inheritance>] "{" <slotDefs> "}"


<mixinDef> := <typeHeader> "mixin" <id> [<inheritance>] "{" <slotDefs> "}"
<facetDef> := <typeHeader> "facet" "class" <id> [<inheritance>] "{" <slotDefs> "}"
<enumDef> := <typeHeader> "enum" "class" <id> [<inheritance>] "{" <enumValDefs>
<slotDefs> "}"

<typeHeader> := [<doc>] <facets> <typeFlags>


<typeFlags> := <typeFlag>*
<typeFlag> := <protection> | "abstract" | "final" | "const" | "native"
<protection> := "public" | "protected" | "private" | "internal"
<inheritance> := ":" <typeList>

Slot Def
<enumValDefs> := <enumValDef> ("," <enumValDef>)* <eos>
<enumValDef> := <id> ["(" <args> ")"]

<slotDefs> := <slotDef>*
<slotDef> := <fieldDef> | <methodDef> | <ctorDef> | <staticInit>

<fieldDef> := <facets> <fieldFlags> <type> <id> [":=" <expr>]


[ "{" [<fieldGetter>] [<fieldSetter>] "}" ] <eos>
<fieldFlags> := <fieldFlag>*
<fieldFlag> := <protection> | "abstract" | "const" | "final" | "native" |
"override" | "readonly" | "static" | "virtual"
<fieldGetter> := "get" (<eos> | <block>)
<fieldSetter> := <protection> "set" (<eos> | <block>)

<methodDef> := <facets> <methodFlags> <type> <id> "(" <params> ")" <methodBody>


<methodFlags> := <methodFlag>*
<methodFlag> := <protection> | "abstract" | "native" | "once" |
"override" | "static" | "virtual" | "final"
<params> := [<param> ("," <param>)*]
<param> := <type> <id> [":=" <expr>]
<methodBody> := <eos> | ( "{" <stmts> "}" )

<ctorDef> := <facets> <ctorFlags> "new" <id> "(" <params> ")" [ctorChain]


<methodBody>
<ctorFlags> := [<protection>]
<ctorChain> := ":" <ctorChainThis> | <ctorChainSuper>
<ctorChainThis> := "this" "." <id> "(" <args> ")"
<ctorChainSuper> := "super" ["." <id>] "(" <args> ")"

<staticInit> := "static" "{" <stmts> "}"

Facets
<facets> := <facet>*
<facet> := "@" <simpleType> [<facetVals>]
<facetVals> := "{" <facetVal> (<eos> <facetVal>)* "}"
<facetVal> := <id> "=" <expr>

Stmt
<block> := <stmt> | ( "{" <stmts> "}" )
<stmts> := <stmt>*
<stmt> := <break> | <continue> | <for> | <if> | <return> | <switch> |
<throw> | <while> | <try> | <exprStmt> | <localDef> | <itAdd>
<break> := "break" <eos>
<continue> := "continue" <eos>
<for> := "for" "(" [<forInit>] ";" [<expr>] ";" [<expr>] ")" <block>
<forInit> := <expr> | <localDef>
<if> := "if" "(" <expr> ")" <block> [ "else" <block> ]
<return> := "return" [<expr>] <eos>
<throw> := "throw" <expr> <eos>
<while> := "while" "(" <expr> ")" <block>
<exprStmt> := <expr> <eos>
<localDef> := [<type>] <id> [":=" <expr>] <eos>
<itAdd> := <expr> ("," <expr>)* [","] <eos>

<try> := "try" <block> <catch>* [<finally>]


<catch> := "catch" [<catchDef>] <block>
<catchDef> := "(" <type> <id> ")"
<finally> := "finally" <block>

<switch> := "switch" "(" <expr> ")" "{" <case>* [<default>] "}"


<case> := "case" <expr> ":" <stmts>
<default> := "default" ":" <stmts>

Expr
<expr> := <assignExpr>
<assignExpr> := <ifExpr> [<assignOp> <assignExpr>]
<assignOp> := "=" | "*=" | "/=" | "%=" | "+=" | "-="

<ifExpr> := <ternaryExpr> | <elvisExpr>


<ternaryExpr> := <condOrExpr> ["?" <ifExprBody> ":" <ifExprBody>]
<elvisExpr> := <condOrExpr> "?:" <ifExprBody>
<ifExprBody> := <condOrExpr> | <ifExprThrow>
<ifExprThrow> := "throw" <expr>

<condOrExpr> := <condAndExpr> ("||" <condAndExpr>)*


<condAndExpr> := <equalityExpr> ("&&" <equalityExpr>)*
<equalityExpr> := <relationalExpr> [("==" | "!=" | "===" | "!==") <relationalExpr>]
<relationalExpr> := <typeCheckExpr> | <compareExpr>
<typeCheckExpr> := <rangeExpr> [("is" | "as" | "isnot") <type>]
<compareExpr> := <rangeExpr> [("<" | "<=" | ">" | ">=" | "<=>") <rangeExpr>]
<rangeExpr> := <addExpr> ((".." | "..<") <addExpr>)*
<addExpr> := <multExpr> (("+" | "-") <multExpr>)*
<multExpr> := <parenExpr> (("*" | "/" | "%") <parenExpr>)*
<parenExpr> := <unaryExpr> | <castExpr> | <groupedExpr>
<castExpr> := "(" <type> ")" <parenExpr>
<groupedExpr> := "(" <expr> ")" <termChain>*
<unaryExpr> := <prefixExpr> | <termExpr> | <postfixExpr>
<prefixExpr> := ("!" | "+" | "-" | "++" | "--") <parenExpr>
<postfixExpr> := <termExpr> ("++" | "--")

<termExpr> := <termBase> <termChain>*


<termBase> := <literal> | <idExpr> | <typeBase>
<typeBase> := <typeLiteral> | <slotLiteral> | <namedSuper> | <staticCall> |
<dsl> | <closure> | <simple> | <ctorBlock>
<typeLiteral> := <type> "#"
<slotLiteral> := [<type>] "#" <id>
<namedSuper> := <type> "." "super"
<staticCall> := <type> "." <slotExpr>
<dsl> := <type> "<|" <anyChar>* "|>"
<simple> := <type> "(" <expr> ")"
<ctorBlock> := <type> <itBlock>
<termChain> := <dotCall> | <dynCall> | <safeDotCall> | <safeDynCall> |
<indexExpr> | <callOp> | <itBlock>
<itBlock> := "{" <stmts> "}"
<dotCall> := "." <slotExpr>
<dynCall> := "->" <slotExpr>
<safeDotCall> := "?." <slotExpr>
<safeDynCall> := "?->" <slotExpr>
<idExpr> := <local> | <slotExpr>
<slotExpr> := <field> | <call>
<local> := <id>
<field> := ["&"] <id>
<call> := <id> ["(" <args> ")"] [<closure>]
<args> := [<expr> ("," <expr>)*]
<indexExpr> := "[" <expr> "]"
<callOp> := "(" <args> ")" [<closure>]

<literal> := "null" | "this" | "super" | "it" |


<bool> | <int> | <float> | <decimal> | <str> |
<duration> | <uri> | <list> | <map> | <uri>
<list> := [<type>] "[" <listItems> [","] "]"
<listItems> := "," | (<expr> ("," <expr>)*)
<map> := [<mapType>] "[" <mapItems> [","] "]"
<mapItems> := ":" | (<mapPair> ("," <mapPair>)*)
<mapPair> := <expr> ":" <expr>

<closure> := <closureSig> "{" <stmts> "}"


<closureSig> := "|" <closureParams> ["->" <type>] "|"
<closureParams> := [<closureParam> ("," <closureParam>)*]
<closureParam> := <formal> | <id>

See Literals for grammar of the literal tokens.

Type
<type> := <nullType> | <nonNullType>
<nullType> := <nonNullType> "?"
<nonNullType> := <simpleType> | <listType> | <mapType> | <funcType>
<typeList> := <type> ("," <type>)*
<simpleType> := <id> ["::" <id>]
<listType> := <type> "[]"
<mapType> := ["["] <type> ":" <type> ["]"]
<funcType> := "|" [formals] ["->" <type>] "|"
<formals> := [<formal> ("," <formal>)*]
<formal> := <formalFull> | <formalInferred> | <formalTypeOnly>
<formalFull> := <type> <id>
<formalInferred> := <id>
<formalTypeOnly> := <type>
Misc
<id> := <idStart> (idChar)*
<idStart> := A-Z | a-z | _
<idChar> := A-Z | a-z | _ | 0-9
<eos> := ; | \n | }

Keywords
abstract foreach return
as if static
assert internal super
break is switch
case isnot this
catch it throw
class mixin true
const native try
continue new using
default null virtual
do once volatile
else override void
false private while
final protected
finally public
for readonly

Position Keyword
enum
facet

You might also like