Types and Values¶
Simple values look similar to values from JavaScript, Java, C#.
Type names consistently start with a capital letter though:
- Boolean✓ ; not- boolean✗
- Int✓ ; not- int✗
- String✓ ; not- string✗
Value names, like lower-variable names,
start with a lower-case letter: false✓ ; not False✗.
class Boolean¶
A Boolean is a truth value that may be used in if and loop conditions.
Boolean extends AnyValue, Equatable
methods¶
Boolean.constructor¶
Boolean.toString¶
class Int32¶
Int32 is the default, general-use signed integer value type in Temper. Because it is so frequently used, the alias Int is encouraged in source code.
Int Syntax Examples¶
Integers can be runs of decimal digits.
123
// ✅ 123
Zero is a valid number, but C-style octal literals (with zero padding) would be a source of confusion.
0
// ✅ 0
let hundred = 100;
let ten     = 010; // <!-- No
let one     = 001; // <!-- Still no
// ❌ Interpreter encountered error()!
You can't use commas in a number literal to make large numbers readable, but you can use an underscore to separate digit groups.
[123,456,789] == [123 , 456 , 789] &&  // Commas separate elements
[123_456_789] == [123456789]
// ✅
Exponential notation is fine for floating point values, but not for integers.
1e2 == 100.0
// ✅
And feel free to use a base like hexadecimal or binary when that fits what you're modelling.
0x10 == 16 && // Hex
0b10 ==  2 && // Binary
0o10 ==  8
// ✅
Int32 extends AnyValue, MapKey
methods¶
Int32.constructor¶
Int32.toFloat64¶
Int32.toInt64¶
Int32.toString¶
:fn(Int32, Int32 | Null):StringSupports radix 2 through 36. Doesn't prefix
+for positive.
Int32.min¶
Int32.max¶
class Int64¶
Int64 extends AnyValue
methods¶
Int64.constructor¶
Int64.toFloat64¶Bubbles if the result isn't within the exponent error of the original.
Int64.toFloat64Unsafe¶If outside bounds, returns a backend-dependent value. Use fuzz testing.
Int64.toInt32¶Bubbles if the result isn't within bounds. TODO Should this panic instead since this is an easy check? TODO But many others cost more to check the panic on fail.
Int64.toInt32Unsafe¶If outside bounds, returns a backend-dependent value. Use fuzz testing. TODO Ensure wrapping here since we do that elsewhere? TODO For symmetry, define consistent semantics for all lossy conversions?
Int64.toString¶
:fn(Int64, Int32 | Null):StringSupports radix 2 through 36. Doesn't prefix
+for positive.
Int64.min¶
Int64.max¶
class Float64¶
A Float64 is an IEEE-754 64-bit (aka double-precision) floating point number.
Syntax for Float64Literal¶
Float64 Syntax Examples¶
A number with a decimal point is a Float64.
123.456
// ✅ 123.456
You can make big numbers more readable by separating digits with underscore(_).
123_456.789 == 123.456_789e3
// ✅
A number with an exponent is a Float64 even if it does not have a decimal point. The exponent follows letter 'e', either upper or lower-case.
123e2 == 12_300.0 &&
123e2 == 123E2
// ✅
Exponents may have a sign.
125e+2     == 12_500.0 &&
1.25e+2    == 125.0 &&
125e-2     == 1.25 &&
125e-0_002 == 1.25  // Exponents are rarely big, but you may break them up.
// ✅
An integer-like number with a decimal suffix is a Float64.
1F64 == 1.0 &&
1f64 == 1.0
// ✅
Unlike in C, digits are required after a decimal point.
1.
// ❌ Operator Dot expects at least 2 operands but got 1!
Which allows Temper to more flexibly combine numbers with class-use syntax.
64.toString(16 /* hex radix */) == "40"
// That '.' is not a decimal point, so that's an integer.
// ✅
Temper also does not recognize all C's number suffixes.
1F
// ❌ Expected subtype of Top, but got Invalid!
Float64 extends AnyValue, Equatable
methods¶
Float64.constructor¶
Float64.toInt32¶Truncates toward zero. Bubbles if the value isn't within 1 of Int32 limits.
Float64.toInt32Unsafe¶If outside bounds, returns a backend-dependent value. Use fuzz testing.
Float64.toInt64¶Truncates toward zero. Bubbles if the value isn't within plus or minus 0x1f_ffff_ffff_ffff, inclusive.
Float64.toInt64Unsafe¶If outside bounds, returns a backend-dependent value. Use fuzz testing.
Float64.toString¶
Float64.abs¶
Float64.acos¶
Float64.asin¶
Float64.atan¶
Float64.atan2¶
:fn(Float64, Float64):Float64The y coordinate is this object.
Float64.ceil¶
Float64.cos¶
Float64.cosh¶
Float64.exp¶
Float64.expm1¶
Float64.floor¶
Float64.log¶
Float64.log10¶
Float64.log1p¶
Float64.max¶
:fn(Float64, Float64):Float64Result is NaN if either is NaN.
Float64.min¶
:fn(Float64, Float64):Float64Result is NaN if either is NaN.
Float64.near¶
:fn(Float64, Float64, Float64 | Null, Float64 | Null):BooleanMatches semantics of Python's math.isclose.
Float64.round¶
Float64.sign¶
Float64.sin¶
Float64.sinh¶
Float64.sqrt¶
Float64.tan¶
Float64.tanh¶statics¶
Float64.e¶
static:Float64
Float64.pi¶
static:Float64
class String¶
A String is a chunk of textual content.
To encourage code with repeatable behavior, strings do not have lengths; the length of a string depends on the kind of characters, which differ across programming languages. See the documentation for string indices to see how to compare character counts and understand more about semantic consistency of string operations.
String values have a syntax similar to JavaScript, but instead of using backticks ` for
multi-line strings with interpolation, use triple-quotes: """ for multi-line strings.
Multi-quoted strings start with 3 "'s.
Each content line must start with a ", called a margin-quote,
which is not part of the content.
"3 quotes" ==
  """
  "3 quotes
// ✅
When a non-blank line doesn't start with a margin-quote, the multi-quoted string ends.
Quotes can be embedded inside a multi-quoted strings.
(
  "Alice said\n\"Hello, World!\"" ==
    """
    "Alice said
    ""Hello, World!"
)
// ✅
Multi-quoted strings may contain interpolations.
let whom = """
    "World
;
"Hello, World!" == """
  "Hello, ${whom}!
// ✅
Escape Sequences¶
Escape sequences in Temper are roughly like those in C, Java, JavaScript, JSON, etc.
The following escape sequences are recognized in strings:
| Escape | Meaning | 
|---|---|
| \0 | Codepoint 0, NUL | 
| \\ | A single backslash | 
| \/ | A forward-slash | 
| \" | A double-quote | 
| \' | A single-quote | 
| \` | A back-quote | 
| \{ | A left curly-bracket | 
| \} | A right curly-bracket | 
| \$ | A dollar sign | 
| \b | A backspace | 
| \t | A tab | 
| \n | A line-feed a.k.a. new-line | 
| \v | A vertical tab | 
| \f | A form-feed | 
| \r | A carriage-return | 
| \uxxxx | A four-digit hex escape sequence | 
| \u{xxxxx,xxx} | One or more comma-separated hex code-points | 
| \ | Broken escape | 
Incidental spaces in multi-line strings¶
When a string spans multiple lines, some space is significant; it contributes to the content of the resulting string value. Spaces that do not contribute to the content are called incidental spaces. Incidental spaces include:
- those used for code indentation, and
- those that appear at the end of a line so are invisible to readers, and often automatically stripped by editors, and
- carriage returns which may be inserted or removed depending on whether a file is edited on Windows or UNIX.
Normalizing incidental space steps include:
- Removing leading space on each line that match the indentation of the close quote.
- Removing the newline after the open quote, and before the close quote.
- Removing space at the end of each line.
- Normalizing line break sequences CRLF, CR, and LF to LF.
For the purposes of identifying incidental space, we imagine that any
interpolation ${...}, scriptlet {:...:}, or hole ${} contributes
1 or more non-space, non-line-break characters.
Indentation matching the close quote is incidental, hence removed.
"""
    "Line 1
    "Line 2
== "Line 1\nLine 2"
// ✅
Each content line is stripped up to and including the margin character. It's good style to line up the margin characters, but not necessary.
"""
    " Line 1
   "  Line 2
    "   Line 3
== " Line 1\n  Line 2\n   Line 3"
// ✅
It's an error if a line is not un-indented from the close quote.
"""
    "Line 1
     Line 2 missing margin character
    "Line 3
// ❌ Missing close quote!, Expected a TopLevel here!
Spaces are removed from the end of a line, but not if there is an interpolation or hole:
"""
    "Line 1  ${"interpolation"}
    "Line 2  ${/*hole*/}
    "Line 3
    == "Line 1  interpolation\nLine 2  \nLine 3"
// ✅
For the purpose of this, space includes:
- Space character: U+20 ' '
- Tab character: U+9 '\t'
A line consists of any maximal sequence of characters other than CR (U+A '\n') and LF (U+D '\r').
A line break is any of the following sequences:
- LF
- CR
- CR LF
Strings may contain embedded expressions.  When a
string contains a ${ followed by an expression,
followed by a }, the resulting string value
is the concatenation of the content before,
content from the expression, and the content after.
"foo ${"bar"} baz"
== "foo bar baz"
// ✅
Interpolated values that aren't String instances
have .toString() called on them automatically,
which is convenient for types that define toString,
such as Int.
let two = 2;
"one ${two} three"
== "one 2 three"
// ✅
An empty interpolation contributes no characters, which means it may be used to embed meta-characters.
"$${}{}" == "\$\{\}"
// ✅
(This mostly comes in handy with tagged strings to give fine-grained control over what the tag receives.)
Empty interpolations can also be used to wrap a long string across multiple lines.
"A very long string ${
  // Breaking this string across multiple lines.
}that runs on and on"
== "A very long string that runs on and on"
// ✅
Empty interpolations also let you include spaces at the end of a line in a multi-quoted string.
"""
"Line 1
"Line 2 ${}
== "Line 1\nLine 2 "
// ✅
String extends AnyValue, MapKey
properties¶
String.isEmpty¶
:BooleanTrue iff this string has no code-points.
String.end¶The index at the end of this string, just past the last character if any.
// An empty string's end is the same as its start. "".end.compareTo(String.begin) == 0 && // Any other string's end is after its start. "foo".end.compareTo(String.begin) > 0 // ✅methods¶
String.constructor¶
String.get¶
:fn(String, StringIndex):Int32The integer value of the codepoint at the given index or panics if there is no such value.
This may produce an unexpected result if index was derived from operations involving a different string. For example, within a language that uses UTF-16 surrogates to represent supplementary code points, using a string index from another string might point into the middle of a code-point in this string causing this method to return the integer value of the trailing surrogate instead of the whole code point.
"foo"[String.begin] == char'f' // ✅
String.hasIndex¶
:fn(String, StringIndex):BooleanTrue when index points to a character in this string. False if it is out of bounds.
!"".hasIndex(String.begin) && "foo".hasIndex(String.begin) // ✅
String.indexOf¶
:fn(String, String, StringIndex | Null):StringIndexOptionThe first index of target if found, starting from start.
String.next¶
:fn(String, StringIndex):StringIndexAn index for the next code-point after the one pointed to by index in this string. Returns end if there is no such code-point.
let s = "abc"; let i = s.next(String.begin); s[i] == char'b' // ✅
String.prev¶
:fn(String, StringIndex):StringIndexAn index for the code-point that preceded the one pointed to by index in this string. Returns begin if there is no such code-point.
let s = "abc"; let i = s.prev(s.end); s[i] == char'c' // ✅
String.step¶
:fn(String, StringIndex, Int32):StringIndexAn index for by steps away from the given index, either positive/forward or negative/backward. If forward but there's no such code-point, returns end, or else begin for no such code-point when stepping backward.
String.hasAtLeast¶
:fn(String, StringIndex, StringIndex, Int32):BooleanTrue if there are at least minCount code-points between begin and end (exclusive). Zero if begin is at or past end.
s.hasAtLeast(begin, end, n)is at least as efficient ass.countBetween(begin, end) >= nand better in many situations.When the native string representation is UTF-8, and the number of octets between begin and end is minCount×4 or greater, hasAtLeast requires no counting.
When the native string representation is UTF-16, and the number of code units between begin and end is minCount×2 or greater, hasAtLeast requires no counting.
At no point is it worse than O(min(n, minCount)) where n is the actual count between begin and end.
let s = "abcdefghijklmnopqrstuvwxyz"; let i = s.next(String.begin); // Points at 'b' let j = s.prev(s.end); // Points at 'y' // Between i and j in s are 26-2, 24 letters. s.hasAtLeast(i, j, 4) && // Fast path for all native string encodings s.hasAtLeast(i, j, 24) && // Might require counting !s.hasAtLeast(i, j, 25) && // Might require counting !s.hasAtLeast(i, j, 100) // Fast path too // ✅
String.countBetween¶
:fn(String, StringIndex, StringIndex):Int32The count of codepoints between begin and end. On most backends, this is O(n) on the output size. Use
String.hasAtLeastinstead when trying to test that there are at least a number of codepoints as it can more efficient for many strings.let s = "abcdefghijklmnopqrstuvwxyz"; let i = s.next(String.begin); // Points at 'b' let j = s.prev(s.end); // Points at 'y' s.countBetween(i, j) == (26 - 2) // ✅
String.slice¶
:fn(String, StringIndex, StringIndex):StringThe string containing all and only the code points between the given indices in this string in order. If begin > end, returns the empty string.
let s = "tsubo"; let i = s.next(String.begin); let j = s.prev(s.end); "sub" == s.slice(i, j) // ✅
String.split¶
:fn(String, String):List<String>Where splitting by "" returns each code point as a separate string.
String.forEach¶
:fn(String,fn(Int32):Void):VoidInvokes body with each code point value in order.
for (let codePoint: Int of "foo-bar") { console.log("U+${codePoint.toString(16)}"); } //!outputs "U+66" //!outputs "U+6f" //!outputs "U+6f" //!outputs "U+2d" //!outputs "U+62" //!outputs "U+61" //!outputs "U+72" // ✅
String.toFloat64¶
:fn(String):Float64 | BubbleSupports numeric JSON format plus Infinity and NaN.
String.toInt32¶
:fn(String, Int32 | Null):Int32 | BubbleSupports integer JSON format, plus any radix 2 through 36.
String.toInt64¶
:fn(String, Int32 | Null):Int64 | BubbleSupports integer JSON format, plus any radix 2 through 36.
String.toString¶statics¶
String.fromCodePoint¶
static:fn(Int32):String | BubbleConvert the single code point to a string if it's valid. Specifically, it must be a Unicode scalar value, which is between 0 and 0x10FFFF, inclusive, except for the surrogate range 0xD800 through 0xDFFF.
String.fromCodePoints¶
static:fn(Listed<Int32>):String | BubbleConvert the list of code points to a string if they're valid.
String.begin¶
static:StringIndexA string index that works for the start of any string.
class StringBuilder¶
A StringBuilder allows composing a string by appending substrings at the end.
StringBuilder extends AnyValue
methods¶
StringBuilder.constructor¶
:fn(StringBuilder):Void
StringBuilder.append¶
:fn(StringBuilder, String):VoidYou can append a string at the end.
StringBuilder.appendCodePoint¶
:fn(StringBuilder, Int32):Void | BubbleA single code point can be appended. If it's not a valid Unicode scalar value, the call bubbles. This operation can be more efficient on some backends than making a temporary string from a code point just for appending.
StringBuilder.appendBetween¶
:fn(StringBuilder, String, StringIndex, StringIndex):VoidappendBetween can be connected to a function that appends a substring without creating a new backing array for the substring.
StringBuilder.toString¶
:fn(StringBuilder):String
interface StringIndexOption¶
A string index, or a similar value that idiomatically represents no string index.
String helpers like indexOf in many languages can return an integer like value which is either a position within the string or an integer value that represents failure to find a match like -1.
StringIndexOption combines those two options in a type-safe way that typically translates to an idiomatic integer.
StringIndexOption extends AnyValue, Equatable
methods¶
StringIndexOption.compareTo¶
:fn(StringIndexOption, StringIndexOption):Int32Sorts
StringIndex.nonebefore every valid string index.Comparing string indices derived from different strings produces a behaviour that is stable within the language but which may differ across translation targets with different internal string representations.
class StringIndex¶
A string index represents a position within a string, at a character or at its end.
String indices translate to integral types (usually int or size_t) but the exact integer
value for any notional index may differ from language to language.
Strings are similar to arrays of "characters" in many languages, but the kind of character differs from language to language.
For example a string like "κόσμε𐆊" can be represented in multiple ways.
| ↓Encoding/ Glyph→ | κ | ό | σ | μ | ε | 𐆊 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| UTF-8 | ce | ba | e1 | bd | b9 | cf | 83 | ce | bc | ce | b5 | f0 | 90 | 86 | 8a | 
| UTF-16 | 3ba | 1f79 | 3c3 | 3bc | 3b5 | d800 | dd8a | ||||||||
| UTF-32 | 3ba | 1f79 | 3c3 | 3bc | 3b5 | 1018a | |||||||||
Go, and Rust and many C and C++ codebases use UTF-8 which can have between one to four bytes for a code-point. The start of the n-th code-point could be anywhere between the n-th byte and the 4*n-th byte. Figuring out exactly where involves examining n bytes, an O(n) operation.
C# and Java use UTF-16 which represent most code-points using one 2-byte code unit, but may use a pair of "surrogates."
Python3 presents strings as code-points but uses tricks internally to use fewer than 3-bytes per code-point of storage for most strings.
As can be seen above, O(1) random access won't work across languages. The StringIndex type allows an efficient representation of a position within a string. Each StringIndex is either the start of a string or is derived from an operation that takes into account the string content. This lets Temper provide consistent, efficient string operations regardless of the native character size.
StringIndex extends AnyValue, StringIndexOption
statics¶
StringIndex.none¶
static:NoStringIndexThe singleton NoStringIndex value
class NoStringIndex¶
The absence to find a string index.
As noted in StringIndexOption, this is an integer-like type that represents the failure to find a valid string index.
NoStringIndex extends AnyValue, StringIndexOption
interface Console¶
A Console provides logging and diagnostic capabilities. It might be configured differently depending on the backend and environment. Don't rely on it being attached to any standard stream.
Use the builtin instance console for default logging needs.
Console extends AnyValue
methods¶
Console.log¶Log a message at default informational level to this console.
class List¶
The List type is a random-access, immutable sequence of values. List
literals using [...] syntax represent immutable List instances. For
mutable random-access sequences, see ListBuilder.
See also Listed for recommendations on List vs Listed use.
List <T> extends AnyValue, Listed<T>
typeFormals¶
List.<T>¶
outextendsAnyValueproperties¶
List.length¶
:Int32methods¶
List.constructor¶
List.get¶Panic on index out of bounds.
List.toList¶
List.toListBuilder¶
:fn(List<T>):ListBuilder<T>
List.forEach¶
:fn(List<T>,fn(T):Void):Voidstatics¶
List.of¶
static:fn<T>(...T):List<T>Same as
[...]syntax but provides an explicit type argument.
class ListBuilder¶
The ListBuilder type is a random-access, mutable, growable sequence of values.
ListBuilder <T> extends AnyValue, Listed<T>
typeFormals¶
ListBuilder.<T>¶
extendsAnyValueproperties¶
ListBuilder.length¶
:Int32methods¶
ListBuilder.constructor¶
:fn(ListBuilder<T>):Void
ListBuilder.add¶
:fn(ListBuilder<T>, T, Int32 | Null):VoidPanics for
atless than 0 or greater thanlength.
ListBuilder.addAll¶
:fn(ListBuilder<T>, Listed<T>, Int32 | Null):VoidPanics for
atless than 0 or greater thanlength.
ListBuilder.clear¶
:fn(ListBuilder<T>):VoidRemoves all values, retaining internal capacity where possible.
ListBuilder.removeLast¶
:fn(ListBuilder<T>):TRemoves the last value if any, and returns it. Expected to be constant time. Panics if empty.
ListBuilder.reverse¶
:fn(ListBuilder<T>):VoidReverses this list builder in place.
ListBuilder.set¶
:fn(ListBuilder<T>, Int32, T):VoidPanics on index out of bounds.
ListBuilder.sort¶
:fn(ListBuilder<T>,fn(T, T):Int32):VoidSorts in place. Uses stable sorting.
ListBuilder.splice¶
:fn(ListBuilder<T>, Int32 | Null, Int32 | Null, Listed<T> | Null):List<T>Removes
removeCountitems starting atindex, or until the end of the current items. Returns the removed items, andnewValuesare put in their place. Theindexis clamped to current bounds.
ListBuilder.toList¶
:fn(ListBuilder<T>):List<T>
ListBuilder.toListBuilder¶
:fn(ListBuilder<T>):ListBuilder<T>
interface Listed¶
The Listed type is an immutable view of a random-access sequence of values.
A Listed<String> may contain strings, a Listed<Boolean> may contain
booleans, etc.
The backing object might or might not be mutable. During a function call, an
argument of type Listed shouldn't be modified concurrently, although it
might be modified after the end of the call, so receivers shouldn't keep
any references to a Listed parameter for later use, unless accounting for
possible changes or according to specific contracts defined for particular
use cases.
If you want to keep a reference to a Listed argument, it's often best to
call toList on it and retain that value instead.
Usually, don't return type Listed. It's usually best to return type List.
typeFormals¶
Listed.<T>¶
extendsAnyValueproperties¶
Listed.length¶
:Int32The length of a listed is the number of items it contains.
let myList: List<String> = ["foo", "bar"]; myList.length == 2 // ✅
Listed.isEmpty¶
:BooleanTrue if this contains no elements.
methods¶
Listed.get¶The ith element, or Bubble if i is outside the range [0, [length]).
let ls = ["a", "b", "c"]; // List of length 3 // The list is zero-indexed. console.log(ls[0]); //!outputs "a" console.log(ls[1]); //!outputs "b" // ✅Out of bounds results in panic. Check in advance when handling indices of uncertain origin. Panics can't be handled by code in Temper, although some backend languages can handle their particular implementations of panic.
// console.log(ls[-1]); // console.log(ls[ 3]); // ⏸️
Listed.getOr¶
Listed.slice¶
:fn(Listed<T>, Int32, Int32):List<T>A new list containing all and only elements from [begin] inclusive to just before [end] exclusive.
Listed.toList¶Provides an immutable
Listwith the values of thisListed. If this is already aList, typically just returnsthiswithout any copying.
Listed.toListBuilder¶
:fn(Listed<T>):ListBuilder<T>Always creates a new object and backing buffer even if this is already a
ListBuilder.
Listed.mapDropping¶
:fn<O>(Listed<T>,fn(T):O | Bubble):List<O>Maps elements using [transform] and filtering out elements for which [transform] fails.
Listed.map¶
:fn<O>(Listed<T>,fn(T):O):List<O>Maps elements using [transform].
Listed.filter¶
:fn(Listed<T>,fn(T):Boolean):List<T>
Listed.reduce¶
:fn(Listed<T>,fn(T, T):T):TReduce to a single value, starting from the first item in this list. Panic if empty.
Listed.reduceFrom¶
:fn<O>(Listed<T>, O,fn(O, T):O):OReduce to a single value, starting from the given initial value.
Listed.reduceFromIndex¶
:fn<O>(Listed<T>, O, Int32,fn(O, T):O):O
Listed.join¶
:fn(Listed<T>, String,fn(T):String):String
Listed.sorted¶
:fn(Listed<T>,fn(T, T):Int32):List<T>Returns a sorted version of this listed. Uses stable sorting.
class Map¶
A Map is a read-only key-value collection.
Map <K, V> extends AnyValue, Mapped<K, V>
typeFormals¶
Map.<K>¶
Map.<V>¶
outextendsAnyValuemethods¶
Map.constructor¶
:fn(Map<K, V>, List<Pair<K, V>>):VoidPreserves entry order when accessed from Temper code.
class MapBuilder¶
A MapBuilder is a read-write key-value collection for building Map
objects. Preserves insertion order when accessed from Temper code.
MapBuilder <K, V> extends AnyValue, Mapped<K, V>
typeFormals¶
MapBuilder.<K>¶
MapBuilder.<V>¶
extendsAnyValuemethods¶
MapBuilder.constructor¶
:fn(MapBuilder<K, V>):Void
MapBuilder.clear¶
:fn(MapBuilder<K, V>):VoidRemoves all entries, retaining internal capacity where possible.
MapBuilder.remove¶
:fn(MapBuilder<K, V>, K):V | BubbleRemoves the given entry if found, and returns its value.
MapBuilder.set¶
interface Mapped¶
A Mapped is a read-only view to key-value mapped data, such as a Map or
MapBuilder.
Mapped <K, V> extends AnyValue
typeFormals¶
Mapped.<K>¶
Mapped.<V>¶
extendsAnyValueproperties¶
Mapped.length¶
:Int32The number of key value pairs in the Mapped.
methods¶
Mapped.get¶
:fn(Mapped<K, V>, K):V | BubbleGet the value associated with [key], or
bubble()s if no key is found.
Mapped.getOr¶Get the value associated with [key], falling back to [fallback] when no such key exists.
Mapped.has¶
:fn(Mapped<K, V>, K):BooleanChecks whether [key] is present in the Mapped.
Mapped.keys¶Gets a Listed of all of the keys.
Mapped.values¶Gets a Listed of all of the values.
Mapped.toMap¶It does not need to make a copy if the Mapped is already a Map because neither can be mutated.
Mapped.toMapBuilder¶
:fn(Mapped<K, V>):MapBuilder<K, V>Converts the Mapped to a MapBuilder.
It must make a copy, even when the Mapped is already a MapBuilder so that both can be mutated independently.
Mapped.toList¶
:fn(Mapped<K, V>):List<Pair<K, V>>Identical to
Mapped.toListBuilderexcept for the return value being a List.Constructs a Pair<
Mapped.<K>,Mapped.<V>> for each key and value in the Mapped.
Mapped.toListBuilder¶
:fn(Mapped<K, V>):ListBuilder<Pair<K, V>>Constructs a Pair<
Mapped.<K>,Mapped.<V>> for each key and value in the Mapped.
Mapped.toListWith¶
:fn<T>(Mapped<K, V>,fn(K, V):T):List<T>Similar to
Mapped.toListBuilderWith.Calls [func] on each key and value in the Mapped.
Mapped.toListBuilderWith¶
:fn<T>(Mapped<K, V>,fn(K, V):T):ListBuilder<T>Returns a ListBuilder<T>.
Calls [func] on each key and value in the Mapped.
Mapped.forEach¶
:fn(Mapped<K, V>,fn(K, V):Void):VoidCalls the provided binary function func on each key and value for the side effect
This method is more likely to change or be removed before Temper 1.0 than most
class Pair¶
typeFormals¶
Pair.<K>¶
outextendsAnyValue
Pair.<V>¶
outextendsAnyValueproperties¶
Pair.key¶
:K
Pair.value¶
:Vmethods¶
Pair.constructor¶
interface MapKey¶
Limited at present to String and Int keys but expected to be user-implementable in the future.
MapKey extends AnyValue, Equatable
class Deque¶
A Deque or double-ended queue allows for breadth-first processing via efficient adds at the end and efficient removal from the front.
let deque = new Deque<String>();
deque.add("a");
deque.add("b");
console.log(deque.removeFirst()); //!outputs "a"
deque.add("c");
deque.add("d");
while (!deque.isEmpty) {
  console.log(deque.removeFirst()); //!outputs ["b", "c", "d"]
}
// ✅
typeFormals¶
Deque.<T>¶
extendsAnyValueproperties¶
Deque.isEmpty¶
:BooleanTrue if this contains no elements.
methods¶
Deque.constructor¶
Deque.add¶Add an element at the end.
Deque.removeFirst¶Removes and returns the first element. Panics if empty.
class DenseBitVector¶
A bit vector where the probability of any bit with index in [0, capacity] is high. This does not assume that the probability of a bit being set depends on the probability of an adjacent bit being set.
DenseBitVector extends AnyValue
methods¶
DenseBitVector.constructor¶
:fn(DenseBitVector, Int32):Void
DenseBitVector.get¶
:fn(DenseBitVector, Int32):BooleanThe bit at the given [index]. If a bit has not previously been set then it is assumed to be false.
DenseBitVector.set¶
:fn(DenseBitVector, Int32, Boolean):VoidSets the bit at [index] to [newValue]. After this, [get], given [index], will return [newValue].
let bitVector = new DenseBitVector(16); console.log(bitVector.get(3).toString()); //!outputs "false" bitVector.set(3, true); console.log(bitVector.get(3).toString()); //!outputs "true" bitVector.set(3, false); console.log(bitVector.get(3).toString()); //!outputs "false" // ✅
Function Types¶
The keyword fn is used to denote function values and types.
| Example | Means | 
|---|---|
| fn (): Void | Type for a function that takes no arguments and returns the voidvalue | 
| fn (Int): Int | Type for a function that takes one integer and returns an integer | 
| fn<T> (T): List<T> | Type for a generic function with a type parameter <T> | 
| fn (...Int): Boolean | Type for a function that takes any number of integers | 
Source: Types.kt
interface Function¶
Function extends AnyValue
interface AnyValue¶
The type for any successful value.
All class and interface types, including builtin types like Int32,
are sub-types of this type.
The special type Bubble is not.
AnyValue
interface Equatable¶
Indicates that values can be tested for equality using == and !=.
User-defined types can't currently extend Equatable, but we expect to support
this in the future.
Equatable extends AnyValue
class Null¶
The singleton null value represents the absence of a value of another type.
In Temper, null is a valid value of any nullable type,
any regular type followed by ?.
String? is a valid type for a local variable or function input that might refer to either null or a string value.
Null extends AnyValue, Equatable
class Empty¶
Empty is the type for a stateless singleton value which may be used judiciously to work around some problems in generic programming.
When not solving generic programming problems, prefer Void. Void translates to the idiomatic no-usable-result concept which for many typed functional languages is the same as Empty but for many dynamic languages is the same as Null.
Empty differs from Void in that it can be used as the type of a function input and of a local variable.
Empty is like the "unit" type which is present in functional programming languages; a function may be effectively Void but can instead return Empty allowing its return type to bind to agree with a type parameter.
As seen below, the empty value may be retrieved by calling the empty()
function, and is distinct from Null.
let e: Empty? = empty();
console.log("Empty is not null: ${e != null}");
//!outputs  "Empty is not null: true"
// ✅
Empty extends AnyValue, Equatable
class Void¶
Void
Functional interface¶
Functional interfaces are like SAM types in Java; they let named types correspond to function types.
??? warning
In-progress. The example below does not yet work.
TODO: implement functional interface support and remove SAM-type guessing in be-java.
Then remove the inert marker.
@fun interface Predicate<T>(x: T): Boolean;
// Applying a predicate to a value of its type parameter, returns a boolean.
let applyAndNegate<T>(x: T, predicate: Predicate<T>): Boolean {
  !predicate(x)    // predicate is applied as a function
}
let s = "abcd";
//!outputs: "Is 'abcd' empty? The answer is NOT: true."
console.log("Is '${s}' empty? The answer is NOT: ${
    applyAndNegate(s) { x => x.empty }
    //                ^^^^^^^^^^^^^^^^ That block is-a Predicate<String>
}.");
// ⏸️
The [builtin/@fun] marks the interface above as functional, and allows the abbreviated method-like syntax.
The parts of an abbreviated functional interface declaration are shown below.
// other decorators
@fun
interface InterfaceName<
  // optional type arguments for interface
>(
  // optional arguments for public .apply method
): ReturnType;
// ⏸️
Functional interfaces, aka @fun interfaces, have the following restrictions:
- Their only allowed supertype is [type/AnyValue].
- They may declare only one method, named apply.
- The interface may declare type parameters but the method may not.
- They may not declare properties, getters, setters, or statics.
  This includes members implicitly added by macros like @json.
- A functional interface may not be used with runtime type operators: [builtin/is] or [builtin/as].
Note
These restrictions allow translating functional interface types to nominal types for target languages that only provide those.
Meanwhile, the lack of a type hierarchy and the restrictions on runtime type operations allow translating them to structural function types, e.g. arrow types, for target languages that benefit from those.
Coroutine Types¶
interface GeneratorFn¶
A marker interface for functions that may yield control to their caller even
if they do not lexically mention the # yield() builtin
pseudo-control-flow instruction.
GeneratorFn extends AnyValue, Function
interface Generator¶
A multi-step computation that may be scheduled for a step by calling the
Generator.next method.
Block lambdas that extend GeneratorFn specify factories for Generators, or if they don't Bubble, for Generators.
Terminology note:
Coroutines may be represented as Generators where
Generator.next
starts or resumes computation and the coroutine pauses itself
when done with the step.
We avoid the term "coroutine" because many programmers associate it
narrowly with a kind of parallelism.
Generator <YIELD> extends AnyValue
typeFormals¶
Generator.<YIELD>¶
outextendsAnyValueproperties¶
Generator.done¶
:BooleanTrue if subsequent calls to
Generator.nextwould do no work and return a DoneResult.That "if" is not an "if and only if" because in the case where a GeneratorFn body yielded just before its return, done may be false but a subsequent call to next would return a DoneResult and reading done after that would get true.
methods¶
Generator.next¶
:fn(Generator<YIELD>):GeneratorResult<YIELD> | BubbleStarts/resumes the computation and runs until it pauses or completes.
If the computation was
Generator.doneprior to the start of the call or it is done at the end, returns DoneResult. This is the case for a GeneratorFn that returns.If the computation pauses, returns the value associated with the decision to pause. For a GeneratorFn, this is the value passed to #
yield()builtin. If a GeneratorFn bubbles, the call to next propagates it.
Generator.close¶Should be called once it is realized that
Generator.nextis not going to be called again to release resources needed by next.After close exits,
Generator.donewill be true.
interface SafeGenerator¶
A variant of Generator that does not bubble when computing the next result.
SafeGenerator <YIELD> extends AnyValue, Generator<YIELD>
typeFormals¶
SafeGenerator.<YIELD>¶
outextendsAnyValuemethods¶
SafeGenerator.next¶
:fn(SafeGenerator<YIELD>):GeneratorResult<YIELD>Starts/resumes the computation and runs until it pauses or completes.
If the computation was
Generator.doneprior to the start of the call or it is done at the end, returns DoneResult. This is the case for a GeneratorFn that returns.If the computation pauses, returns the value associated with the decision to pause. For a GeneratorFn, this is the value passed to #
yield()builtin.
interface GeneratorResult¶
One of a sequence of results from a Generator.
GeneratorResult <YIELD> extends AnyValue
typeFormals¶
GeneratorResult.<YIELD>¶
outextendsAnyValue
class ValueResult¶
A result produced from Generator.next when there is a value to produce and
Generator.done is false.
ValueResult <YIELD> extends AnyValue, GeneratorResult<YIELD>
typeFormals¶
ValueResult.<YIELD>¶
outextendsAnyValueproperties¶
ValueResult.value¶
:YIELDmethods¶
ValueResult.constructor¶
:fn(ValueResult<YIELD>, YIELD):Void
class DoneResult¶
A result produced from Generator.next when there is no value to produce and
Generator.done is true.
DoneResult <YIELD> extends AnyValue, GeneratorResult<YIELD>
typeFormals¶
DoneResult.<YIELD>¶
outextendsAnyValuemethods¶
DoneResult.constructor¶
:fn(DoneResult<YIELD>):Void
Special Types¶
interface Never¶
Never is a type for expressions that will never complete normally.
For example, a loop that never exits, perhaps because it is supposed to process jobs until the end of the embedding program, may use type Never.
Never should not be confused with Empty; Empty admits a valid value which has no content, but Never admits no value.
Unlike ⊥ types in some programming language, there is not a single Never type in Temper. Never<Foo> is not a sub-type of every type; it is only a sub-type of itself and its <Foo>.
Never<String?>? is a valid type and because of the ? nullable notation, that type
admits the special value, null.
Having Never types in Temper allows translating control flow constructs that do not produce "unreachable code" errors.
Temper Never types can translate to bottom types in languages that support that, but Never<String>, for example, can translate to String in languages that do not.
typeFormals¶
Never.<T>¶
outextendsAnyValue
class Bubble¶
Bubble is a type that represents failure to compute a value.
A function declaration may declare it throws Bubble following its return type to indicate that it can fail. When translating to backends that use exceptions, this indicates the function can throw. On other backends, the translation function's return type might be a Result type or a Go-lang extra, error result.
See also orelse.
Bubble extends AnyValue
methods¶
Bubble.constructor¶
Top¶
Top is a super-type of every type, including AnyValue and Bubble.
interface Invalid¶
Invalid is an untranslatable type that represents a failure to properly type an expression. Some backends, notably dynamic language backends, might be able to produce a partially working translation and run tests even in the presence of type errors, allowing developers to narrow down errors, or guide complex refactorings, and the invalid type enables that.
It may be used in meta-programming code, but is not meant for general use by user code.
Invalid
class Problem¶
A Problem is an internal abstraction that represents code that cannot be compiled.
Representing problems in a partially-processed library allows running some tests, giving developers more flexibility in how they investigate what they need to do to get their code production-ready.
Problem extends AnyValue
Type Relationships¶
Note
These type relationships are deprecated. Especially arbitrary type unions and intersections, and unqualified never types.
graph TD
Top --> AnyValue
Top --> Void
Top --> Bubble
AnyValue --> Union["A | B"]
subgraph nominals [Nominals]
  direction LR
  A
  B
end
Union --> A
Union --> B
A --> Intersection["A & B"]
B --> Intersection
Intersection --> Never
Void --> Never
Bubble --> Never
InvalidArrows point from supertypes to subtypes.
There is a Top type at the top which is the super-type of all types.
At the bottom is Never which is the bottom type, a sub-type of all types, and an appropriate type for computations that never complete like
while (true) {}
// ⏸️
The Invalid type, off to the right, is a type marker for constructs that the compiler cannot make sense of. It is outside the type hierarchy; not a subtype of any other nor vice versa.
Top branches into AnyValue, Void, and Bubble. This represents the three ways a computation can complete, either by
- producing an actual value (it produces a sub-type of AnyValue),
- finishing normally but without a usable value, or
- bubbling up the call stack until replaced with a value by orelse.
Below any value we have A | B, a union type.
Values of type A | B can be of type A or1 of type B.
A union type is a super-type of each of its members.
Below the Nominal Type box we have A & B, an
intersection type.
An intersection is a sub-type of each of its elements.
class C extends A, B {}
// ⏸️
In that, C is a declared sub-type of both A and B, so it's a
sub-type of A & B.
(Union and intersection types are actually more general.
A | Bubble is an expressible type as is (A?) & D, so
this diagram does not imply that all union/intersection types fit
neatly in a region on one side of nominal types. Void has
constraints here, however. Void | Bubble makes sense, but
A | Void doesn't.)
In the middle are NominalTypes. These are types declared with a name and parameters. NominalTypes include all these:
Boolean         // A builtin type
List<T?>        // A parameterized type
C               // A nominal type, assuming a definition like `class C` is in scope.
fn (Int): Int   // A type for functions that take an Int and return an Int
// ⏸️
Type Tags¶
Type tags are what determine whether a narrowing cast1 would be safe at runtime.
Static types have more nuance than type tags.
| Static Type | Type Tag | 
|---|---|
| C | C | 
| Boolean | Boolean | 
| List<Int> | List | 
| fn(): Void | Function | 
As you can see, all function values have the same type tag, Function, and generic class's corresponding type tag has the type parameters erased.
Type Compatibility¶
When calling a function method, overloaded methods are filtered based on type and argument list compatibility.
For example, + has variants that operate on Int32 and
Float64.
During type checking, the Temper compiler filters out unusable variants. So if two arguments whose static types are Int32 are passed in, we can filter out the Float64 variants because we know that no Float64 values will ever be passed in.
An input with a static type, I is compatible with a function argument with static type, A when A is not nullable or I is nullable, and any of the following are true:
- I is a functional interface type and A is a block lambda or is a reference to a named function whose signature matches I's apply method signature after substituting I's type parameters.
- I is a type parameter and its bounds are compatible with A.
- A is a type parameter and I is compatible with A*'s bound
- I and A are defined types and A's type shape is or inherits from A's type shape.