What do we want?
Where do we want them?*
In our Pony programs!
Say no more
Every programming language has literals to encode values of certain types, and so does Pony.
In Pony you can express booleans, numeric types, characters, strings and arrays as literals.
true, there is
false. That's it.
Numeric literals can be used to encode any signed or unsigned integer or floating point number.
In most cases Pony is able to infer the concrete type of the literal from the context where it is used (e.g. assignment to a field or local variable or as argument to a method/behaviour call).
It is possible to help the compiler determine the concrete type of the literal using a constructor of one of the numeric types:
- U8, U16, U32, U64, U128, USize, ULong
- I8, I16, I32, I64, I128, ISize, ILong
- F32, F64
let my_explicit_unsigned: U32 = 42_000 let my_constructor_unsigned = U8(1) let my_constructor_float = F64(1.234)
Integer literals can be given as decimal, hexadecimal or binary:
let my_decimal_int: I32 = 1024 let my_hexadecimal_int: I32 = 0x400 let my_binary_int: I32 = 0b10000000000
Floating Point literals are expressed as standard floating point or scientific notation:
let my_double_precision_float: F64 = 0.009999999776482582092285156250 let my_scientific_float: F32 = 42.12e-4
Character literals are enclosed with single quotes (
Character literals, unlike String literals, encode to a single numeric value. Usually this is a single byte, a
U8. But they can be coerced to any integer type:
let big_a: U8 = 'A' // 65 let hex_escaped_big_a: U8 = '\x41' // 65 let newline: U32 = '\n' // 10
The following escape sequences are supported:
\x4Fhex escape sequence with 2 hex digits (up to 0xFF)
Multibyte Character literals
It is possible to have character literals that contain multiple characters. The resulting integer value is constructed byte by byte with each character representing a single byte in the resulting integer, the last character being the least significant byte:
let multiByte: U64 = 'ABCD' // 0x41424344
String literals are enclosed with double quotes
" or triple-quoted
""". They can contain any kind of bytes and various escape sequences:
\u00FEunicode escape sequence with 4 hex digits encoding one code point
\u10FFFEunicode escape sequence with 6 hex digits encoding one code point
\x4Fhex escape sequence for unicode letters with 2 hex digits (up to 0xFF)
Each escape sequence encodes a full character, not byte.
use "format" actor Main new create(env: Env) => let pony = "🐎" let pony_hex_escaped = "p\xF6n\xFF" let pony_unicode_escape = "\U01F40E" env.out.print(pony + " " + pony_hex_escaped + " " + pony_unicode_escape) for b in pony.values() do env.out.print(Format.int[U8](b, FormatHex)) end
All string literals support multiline strings:
let stacked_ponies = " 🐎 🐎 🐎 "
String Literals and Encodings
String Literals contain the bytes that were read from their source code file. Their actual value thus depends on the encoding of their source.
Consider the following example:
let u_umlaut = "ü"
If the file containing this code is encoded as UTF-8 the byte-value of
u_umlaut will be:
\xc3\xbc. If the file is encoded with ISO-8559-1 (Latin-1) its value will be
Triple quoted Strings
For embedding multi-line text in string literals, there are triple quoted strings.
let triple_quoted_string_docs = """ Triple quoted strings are the way to go for long multiline text. They are extensively used as docstrings which are turned into api documentation. They get some special treatment, in order to keep pony code readable: * The string literal starts on the line after the opening triple quote. * Common indentation is removed from the string literal so it can be conveniently aligned with the enclosing indentation e.g. each line of this literal will get its first two whitespaces removed * Whitespace after the opening and before the closing triple quote will be removed as well. The first line will be completely removed if it only contains whitespace. e.g. this strings first character is `T` not `\n`. """
Array literals are enclosed by square brackets. Array literal elements can be any kind of expressions. They are separated by semicolon or newline:
let my_literal_array = [ "first"; "second" "third one on a new line" ]
If the type of the array is not specified, the resulting type of the literal array expression is
Array[T] ref where
T (the type of the elements) is inferred as the union of all the element types:
let my_heterogenous_array = [ U64(42) "42" U64.min_value() ]
In the above example the resulting array type will be
Array[(U64|String)] ref because the array contains
If the variable or call argument the array literal is assigned to has a type, the literal is coerced to that type:
let my_stringable_array: Array[Stringable] = [ U64(0xA) "0xA" ]
my_stringable_array is coerced to
Array[Stringable] ref. This works because
Stringable is a trait that both
It is also possible to return an array with a different Reference Capability than
ref just by specifying it on the type:
let my_immutable_array: Array[Stringable] = [ U64(0xBEEF) "0xBEEF" ]
This way array literals can be used for creating arrays of any Reference Capability.
It is also possible to give the literal a hint on what kind of type it should coerce the array elements to using an
as Expression. The expression with the desired array element type needs to be added right after the opening square bracket, delimited by a colon:
let my_as_array: Array[Stringable] = [ as Stringable: U64(0xFFEF) "0xFFEF" U64(1 + 1) ]
This array literal is coerced to be an
Array[Stringable] ref according to the
If a type is specified on the left-hand side, it needs to exactly match the type in the
Arrays and References
Constructing an array with a literal creates new references to its elements. Thus, to be 100% technically correct, array literal elements are inferred to be the alias of the actual element type. If all elements are of type
T the array literal will be inferred as
Array[T!] ref that is as an array of aliases of the type
It is thus necessary to use elements that can have more than one reference of the same type (e.g. types with
ref capability) or use ephemeral types for other capabilities (as returned from constructors or the consume expression).