Types and Values¶
Simple values look similar to values from JavaScript, Java, C#.
Type names consistently start with a capital letter though:
Boolean
✓ ; notboolean
✗Int
✓ ; notint
✗String
✓ ; notstring
✗
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 Int¶
Int is the general-purpose signed integer value type.
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
// ✅
methods¶
Int.constructor
¶
Int.toFloat64
¶Bubbles if the result isn't within the exponent error of the original.
Int.toFloat64Unsafe
¶If outside bounds, returns a backend-dependent value. Use fuzz testing.
Int.toString
¶Supports radix 2 through 36. Doesn't prefix
+
for positive.
Int.min
¶
Int.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.
1D == 1.0 &&
1d == 1.0
// ✅
Unlike in C, digits are required after a decimal point.
1.
// ❌ Operator Dot expects at least 2 operands but got 1!, Expected function type, but got Invalid!
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
// ❌ Interpreter encountered error()!
Float64 extends
AnyValue, Equatable
methods¶
Float64.constructor
¶
Float64.toInt
¶Truncates toward zero. Bubbles if the error isn't less than 1.
Float64.toIntUnsafe
¶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 or more "
's.
"""
3 quotes
""" == "3 quotes"
// ✅
The end delimiter must match the start. Each delimiter must be on its own line.
""""
A quadruple-quoted string
may embed 3 quotes: """.
"""" == "A quadruple-quoted string\nmay embed 3 quotes: \"\"\"."
// ✅
It's an error to put too many quotes in a row inside a multi-quoted string.
"""
It's illegal to put 3 or more in a triple-quoted string.
Don't do this: """".
"""
// ❌ Overlong multi-quote delimiter not allowed here!, Expected a Expression here!
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 |
\u xxxx |
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"
// ✅
Spaces are stripped equally, so lines may be indented past the close quote.
"""
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 not indented enough
Line 3
"""
// ❌ Cannot strip indentation from multiline string!
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"
// ✅
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):
Int | BubbleThe integer value of the codepoint at the given index or bubbles 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.begin] orelse 0) == 0 // ✅
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.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 start if there is no such code-point.
let s = "abc"; let i = s.prev(s.end); s[i] == char'c' // ✅
String.hasAtLeast
¶
:
fn(
String, StringIndex, StringIndex, Int):
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) >= n
and 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):
IntThe count of codepoints between begin and end. On most backends, this is O(n) on the output size. Use
String.hasAtLeast
instead 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(
Int):
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.toInt
¶
:
fn(
String, Int | Null):
Int | BubbleSupports integer JSON format, plus any radix 2 through 36.
String.toString
¶statics¶
String.fromCodePoint
¶
static
:
fn(
Int):
String | BubbleConvert the single code point to a string if it's valid.
String.fromCodePoints
¶
static
:
fn(
Listed<Int>):
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.
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):
IntSorts
StringIndex.none
before 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>
¶
out
extends
AnyValueproperties¶
List.length
¶
:
Intmethods¶
List.constructor
¶
List.get
¶
:
fn(
List<T>, Int):
T | Bubble
List.toList
¶
List.toListBuilder
¶
:
fn(
List<T>):
ListBuilder<T>
List.forEach
¶
class ListBuilder¶
The ListBuilder type is a random-access, mutable, growable sequence of values.
ListBuilder <
T>
extends
AnyValue, Listed<T>
typeFormals¶
ListBuilder.<T>
¶
extends
AnyValueproperties¶
ListBuilder.length
¶
:
Intmethods¶
ListBuilder.constructor
¶
:
fn(
ListBuilder<T>):
Void
ListBuilder.add
¶
:
fn(
ListBuilder<T>, T, Int | Null):
Void | BubbleBubbles for
at
less than 0 or greater thanlength
.
ListBuilder.addAll
¶
:
fn(
ListBuilder<T>, Listed<T>, Int | Null):
Void | BubbleBubbles for
at
less than 0 or greater thanlength
.
ListBuilder.clear
¶
:
fn(
ListBuilder<T>):
VoidRemoves all values, retaining internal capacity where possible.
ListBuilder.removeLast
¶
:
fn(
ListBuilder<T>):
T | BubbleRemoves the last value if any, and returns it. Expected to be constant time.
ListBuilder.reverse
¶
:
fn(
ListBuilder<T>):
VoidReverses this list builder in place.
ListBuilder.set
¶
:
fn(
ListBuilder<T>, Int, T):
Void
ListBuilder.sort
¶
:
fn(
ListBuilder<T>,fn(
T, T):
Int):
VoidSorts in place. Uses stable sorting.
ListBuilder.splice
¶
:
fn(
ListBuilder<T>, Int | Null, Int | Null, Listed<T> | Null):
List<T>Removes
removeCount
items starting atindex
, or until the end of the current items. Returns the removed items, andnewValues
are put in their place. Theindex
is 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>
¶
extends
AnyValueproperties¶
Listed.length
¶
:
IntThe 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
¶
:
fn(
Listed<T>, Int):
T | BubbleThe 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 Bubble. console.log(ls[-1] orelse "too low"); //!outputs "too low" console.log(ls[ 3] orelse "too high"); //!outputs "too high" // ✅
Listed.getOr
¶
Listed.slice
¶
:
fn(
Listed<T>, Int, Int):
List<T>A new list containing all and only elements from [begin] inclusive to just before [end] exclusive.
Listed.toList
¶Provides an immutable
List
with the values of thisListed
. If this is already aList
, typically just returnsthis
without 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):
T | BubbleReduce to a single value, starting from the first item in this list. Bubble 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, Int,fn(
O, T):
O):
O
Listed.join
¶
:
fn(
Listed<T>, String,fn(
T):
String):
String
Listed.sorted
¶
:
fn(
Listed<T>,fn(
T, T):
Int):
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>
¶
out
extends
AnyValuemethods¶
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>
¶
extends
AnyValuemethods¶
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>
¶
extends
AnyValueproperties¶
Mapped.length
¶
:
IntThe 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.toListBuilder
except 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>
¶
out
extends
AnyValue
Pair.<V>
¶
out
extends
AnyValueproperties¶
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 (true) {
console.log(deque.removeFirst()); //!outputs ["b", "c", "d"]
} orelse void;
// ✅
typeFormals¶
Deque.<T>
¶
extends
AnyValueproperties¶
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.
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 void value |
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 Int,
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 type checked by using the Null type in conjunction with another type.
String | Null 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 Void¶
Void
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>
¶
out
extends
AnyValueproperties¶
Generator.done
¶
:
BooleanTrue if subsequent calls to
Generator.next
would 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.done
prior 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.next
is not going to be called again to release resources needed by next.After close exits,
Generator.done
will 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>
¶
out
extends
AnyValuemethods¶
SafeGenerator.next
¶
:
fn(
SafeGenerator<YIELD>):
GeneratorResult<YIELD>Starts/resumes the computation and runs until it pauses or completes.
If the computation was
Generator.done
prior 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>
¶
out
extends
AnyValue
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>
¶
out
extends
AnyValueproperties¶
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 extends
AnyValue, GeneratorResult<Never>
methods¶
DoneResult.constructor
¶
:
fn(
DoneResult):
Void
Special Types¶
Never¶
Never is a type that represents computations that never complete.
It is a sub-type of every type.
Computations that exit abnormally, for example, by jumping via orelse
, use
Bubble, not Never.
A loop that never exits, perhaps because it is supposed to process jobs until the end of the embedding program, may use type Never.
Bubble¶
Bubble is a type that represents failure to compute a value. Bubble is a sub-type of Top but is not a sub-type of AnyValue so is not a useful type on function input parameters.
See also orelse
.
Top¶
Top is a super-type of every type, including AnyValue and Bubble.
Invalid¶
A type representing a failure to compute a type.
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¶
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
Invalid
Arrows point from super-types to sub-types.
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 | Null) & 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
contraints 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 | Null> // 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¶
Overloaded functions and methods are filtered based on type and argument list compatibility.
For example, +
has variants that operate on Int and
Float64.
During type checking, the Temper compiler filters out unusable variants. So if two arguments whose static type is Int are passed in, we can filter out the Float64 variants because we know that no values whose Type Tags is Float64 will ever be passed in.
An input with a static type, I is compatible with a function argument with static type, A when any of the following are true:
- I is a function type and Function is compatible with A
- A is a function type and I is compatible with Function
- I is a type parameter and its bound is compatible with A
- A is a type parameter and I is compatible with A*'s bound
- I and A are nominal types and A with all its type parameters replaced with wildcards is a sub-type of A with all its type parameters with wildcards.
- I is a union type and any member is compatible with A
- I is an intersection type and all members are compatible with A
- A is a union type and I is compatible with all members of A
- A is an intersection type and I is compatible with any member of A