builtin functions
The fixed library of functions every module already has. There is no import and no backing declaration; they are called like any other function.
const bearings_deg: int32[] = [10, 45, 90];
const count: uint32 = length(bearings_deg);
const widest_deg: int32 = last(bearings_deg);Names
These names are built in: length, last, range, zip, enumerate, float64to32, floor,
ceil, abs, sin, cos, tan, asin, acos, atan, atan2, sqrt, pow, getOrElse,
serialize, and substring.
- A declaration cannot take one of these names.
- Arity and argument types are checked per function, and a mismatch is one diagnostic whatever the function.
- A few are polymorphic over an element type. That polymorphism belongs to the built-ins only, and Flex has no generics of its own.
These names are the whole library, and nothing extends them: an import reaches only modules of your
own project.
Collections
length(xs: T[]) -> uint32— the element count, alwaysuint32whateverTis.last(xs: T[]) -> T— the final element. An empty array is a runtime failure.range(end),range(start, end),range(start, end, step)— an integer array. Every argument is the same integer type, and the result is an array of it.zip(as: A[], bs: B[]) -> (A, B)[]— element-wise pairing, as long as the shorter input.enumerate(xs: T[]) -> (uint32, T)[]— each element paired with its index, from0.
range treats end as exclusive. The one-argument form starts at 0 and steps by 1, the two-argument
form steps by 1, and a step that never reaches end gives an empty array. A step of 0 is a runtime
failure.
const callsigns: string[] = ["Viper-1", "Hawk-2"];
const bearings_deg: int32[] = [10, 45, 90];
length(callsigns) // => 2
last(bearings_deg) // => 90
range((4: uint8)) // => [0, 1, 2, 3]
range((0: int32), (360: int32), (90: int32)) // => [0, 90, 180, 270]
zip(bearings_deg, callsigns) // => [(10, "Viper-1"), (45, "Hawk-2")]
enumerate(callsigns) // => [(0, "Viper-1"), (1, "Hawk-2")]
try(last([]: int32[])) // => noneNumbers
abs(x: T) -> T— absolute value for any numericT. On an unsigned type it is the identity.floor(x: float64) -> Optional<int64>— the largest integer at or belowx, absent when that does not fit in anint64.ceil(x: float64) -> Optional<int64>— the smallest integer at or abovex, absent on the same terms.float64to32(x: float64) -> float32— the narrowing to the nearestfloat32. It is the only supported route between the float types.sqrt(x: float64) -> float64— the square root. A negative argument givesNaNrather than failing.pow(x: float64, y: float64) -> float64—xraised toy.serialize(x: T) -> uint8[]— the big-endian bytes of a numeric value.
serialize needs a type that has a byte width: an intN or uintN whose width is a multiple of 8, a
float32, or a float64. An intN/uintN gives N/8 bytes most-significant first, and a float gives
the bytes of its IEEE-754 pattern. Anything else is rejected at check time, because both the type and
the width are already known.
abs((-120: int32)) // => 120
abs((120: uint32)) // => 120
floor((87.6: float64)) // => some(87)
floor((-2.3: float64)) // => some(-3)
ceil((87.2: float64)) // => some(88)
float64to32((87.5: float64)) // => 87.5
sqrt((16.0: float64)) // => 4
pow((2.0: float64), (8.0: float64)) // => 256
serialize((0x0102: uint16)) // => [1, 2]
serialize((-1: int8)) // => [255]
serialize((87.5: float32)) // => [66, 175, 0, 0]Trigonometry
sin(x: float64) -> float64,cos(x: float64) -> float64,tan(x: float64) -> float64— the argument is in radians.asin(x: float64) -> float64,acos(x: float64) -> float64,atan(x: float64) -> float64— the result is in radians.atan2(y: float64, x: float64) -> float64— the polar angle of(x, y), in radians over(-π, π].
asin and acos outside [-1.0, 1.0] give NaN, which is a value and not a failure. atan2 takes
the ordinate first and the abscissa second.
cos((0.0: float64)) // => 1
asin((1.0: float64)) // => 1.5707963267948966
atan2((1.0: float64), (1.0: float64)) // => 0.7853981633974483
try(asin((2.0: float64))) // => some(NaN)Optionals
getOrElse(x: Optional<T>, fallback: T) -> T— the contained value, or the fallback when there is none.
The first argument must be an Optional, and the fallback must be its inner type.
getOrElse(some((512: uint16)), 0) // => 512
getOrElse((none: Optional<uint16>), (0: uint16)) // => 0Strings
substring(s: string, start: uint32, end: uint32) -> string— the characters over the half-open range[start, end).
Both indices are integer types. A start past end, or an end past the length of the string, is a
runtime failure.
substring("GroundStation-1", 0, 6) // => "Ground"
substring("Viper-1", 6, 7) // => "1"
try(substring("Viper-1", 0, 99)) // => noneErrors
Taking a built-in name for a declaration
const length: uint32 = 3; // error: reserved-builtin-nameAn argument of the wrong type
const count: uint32 = length((42: int32)); // error: builtin-call-argsThe wrong number of arguments
const count: uint32 = length([(1: int32), 2], [(3: int32)]); // error: builtin-call-argsSerializing something with no byte width
const bytes: uint8[] = serialize("Viper-1"); // error: builtin-call-argsSerializing a width that is not a whole number of bytes
const bytes: uint8[] = serialize((1: uint7)); // error: builtin-call-argsRelated
- function — the declaration a built-in name cannot take, and the call form these share.
- value_cast — the numeric conversions that are forms rather than functions.
- array — the collections most of these take.
- optional — what
floor,ceil, andgetOrElsedeal in.