logo

Literals

Overview

Fan is a pure OO language in that everything is an object passed by reference - even fundamental types such as Bool and Int. But some types are a little more special than others because we give them a literal syntax. These types are:

Bool

There are exactly two instances of sys::Bool within the VM which are represented using the true and false keywords.

Int

sys::Int is used to represent a 64-bit signed integer. Fan does not have any integer types for smaller precisions. Fan 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. The runtime guarantees that Int representations between -256 and 1024 are interned to always be the same instance.

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.

Fan 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'

Float

sys::Float is used to represent a 64-bit floating point number. Fan does not have a type for 32-bit floats. Float is const which means that an instance is immutable.

Float literals are expressed like C, Java, etc using a string of decimal digits with a dot and fraction, with an exponent, or using the f suffix. You can use the _ underbar as a separator. Examples of Float literals:

3.0
3f
3.0F
123_456.0
3e6
0.2e+6
1_2.3_7e-5_6

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. Str literals may span multiple lines in which case the newlines are always normalized to \n regardless of how newlines were encoded in the source code text. A couple Str literal examples:

"hello"
"line 1\nline 2"
"It is 73\u00B0 Fahrenheit outside!"

Str literals support string interpolation which allow arbitrary Fan 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 write and easier to read. Fan coding convention is to always use string interpolation rather than concatenation.

If a string literal is prefixed with the "r" character is it a raw string literal. A raw string literal doesn't interpret the "\" or "$" character specially. A raw string cannot contain the " double quote character (since there is no way to escape it). Raw string literals are useful for regular expressions and Window's file paths:

r"c:\somedir\file.txt"
r"\W+"

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. Fan 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)

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 Fan, 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 Fan's subsystem for naming and resolution. Uris have their own literal syntax using the back tick:

`index.html`
`/some/path/file.txt`
`http://fandev.org`
`TPS Report.doc`

Note that when working with URIs in Fan and representing them as literals we always use the decoded format - 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).

Type

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

Str.type
acme::SomeType.type

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

Range

A sys::Range represents a contiguous range of integers from start to end. Ranges may be represented as literals in Fan source code as start..end for an inclusive end or start...end for 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 to operator precedence. These operators are just syntax sugar for constructing a range via sys::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 Objs

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[] - null assumed to be 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.

The empty list is denoted using the special syntax [,]. Often you will specify a type - for example Str[,] is an empty list of strings. If a type is not specified, then the empty list evaluates to a Obj[,].

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", 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.