Introspection
Compile-time queries over symbols, types, records, variants, enums, and static strings. None of these may be overloaded.
Symbol and Function Introspection
Type?
[T]
Type?(#T) : Bool;
true if T is a symbol that names a type.
define foo;
record bar ();
main() {
println(Type?(Type?)); // false
println(Type?(Int32)); // true
println(Type?(foo)); // false
println(Type?(bar)); // true
println(Type?(#3)); // false
}
Symbol?
[x]
Symbol?(#x) : Bool;
true if x names a symbol: a type, record, variant, procedure, intrinsic, or global alias. false for static values such as numbers or static strings.
Operator?
[x]
Operator?(#x) : Bool;
true if x is a symbol declared as an operator (with define (op)). false for ordinary symbols and non-symbols.
StaticCallDefined?
[F, ..T]
StaticCallDefined?(#F, #..T) : Bool;
true if symbol F has an overload matching input types ..T. The first argument must be a symbol (not a callable value).
The library function CallDefined? (from core.operators) wraps this primitive and additionally handles callable record types via StaticCallDefined?(call, F, ..T).
StaticCallOutputTypes
[F, ..T]
StaticCallOutputTypes(#F, #..T); // static types
When you need the output types of a call at compile time, use StaticCallOutputTypes. It resolves which overload of F matches ..T and returns the output types as a multiple-value list. It is a compile error if no matching overload exists.
The library alias CallOutputTypes (from core.operators) wraps this for both symbols and callable types.
StaticMono?
[F]
StaticMono?(#F) : Bool;
true if symbol F has exactly one monomorphic overload (no pattern variables). The counterpart to LambdaMono? for symbols.
StaticMonoInputTypes
[F when StaticMono?(F)]
StaticMonoInputTypes(#F); // static types
For a symbol with exactly one monomorphic overload, the argument types of that overload are available at compile time via StaticMonoInputTypes. It is a compile error if F is not monomorphic.
MainModule
MainModule() : module;
Every Ceramic program has a designated entry-point module. MainModule() returns the module object for it. Useful for writing module-generic test runners:
import test.module.(testMainModule);
main() = testMainModule();
StaticModule
[S]
StaticModule(#S) : module;
To find which module owns a given symbol, call StaticModule. The result is the module object for the module that contains S. It is a compile error if S has no associated module.
ModuleName
[S]
ModuleName(#S); // static string
The fully-qualified module name for the module that contains S is available at compile time as a static string. If S is itself a module, you get that module's own name. It is a compile error if S is not a symbol.
import foo;
import foo.bar as bar;
in baz;
main() {
println(ModuleName(main)); // "baz"
println(ModuleName(foo.a)); // "foo"
println(ModuleName(bar.a)); // "foo.bar"
println(ModuleName(bar)); // "foo.bar"
}
ModuleMemberNames
[M]
ModuleMemberNames(#M); // static strings
To enumerate the public globals of a module, use ModuleMemberNames. It returns every public global in M as a multiple-value list of static strings, in alphabetical order. M must be a module object, for example one obtained from MainModule() or StaticModule(S).
import __primitives__.(MainModule, ModuleMemberNames);
import printer.*;
main() {
println(..ModuleMemberNames(MainModule()));
}
StaticName
[x]
StaticName(#x); // static string
The name of any compile-time value is available as a static string via StaticName. What the string contains depends on what x is:
- Symbol: its name (without module, with parameters).
- Static string: its string value.
- Numeric value: its decimal representation.
- Tuple: comma-delimited inside square brackets (
[a, b, c]).
GetOverload
[F, ..T]
GetOverload(#F, #..T);
When you want to capture a specific overload of a symbol as a callable value, use GetOverload. It selects the overload of F that matches argument types ..T and returns it as a new callable procedure. You can call the result just like any other function. Unlike makeCodePointer, the result is still a fully generic Ceramic callable rather than a fixed function pointer.
define foo;
overload foo(x:Int) { println("Int ", x); }
overload foo(x:Float) { println("Float ", x); }
main() {
GetOverload(foo, Float)(123); // prints: Float 123
}
staticFieldRef
[M, name when StringLiteral?(name)]
staticFieldRef(#M, #name);
To look up a public global by a name that is only known at compile time, use staticFieldRef. The result is the global's value, exactly as if you had written the name directly in code. It is a compile error if name is not a public member of M.
Static String Manipulation
StringLiteral?
[S]
StringLiteral?(#S) : Bool;
true if S is a static string.
stringLiteralByteSize
[S when StringLiteral?(S)]
stringLiteralByteSize(#S) : SizeT;
The length in bytes of static string S is available at compile time via stringLiteralByteSize.
stringLiteralConcat
[..SS when allValues?(StringLiteral?, ..SS)]
stringLiteralConcat(#..SS);
To join several static strings into one at compile time, use stringLiteralConcat. It concatenates all its arguments in order and returns the result as a new static string.
stringLiteralByteSlice
[S, n, m when
StringLiteral?(S)
and n >= 0 and n < stringLiteralByteSize(S)
and m >= 0 and m < stringLiteralByteSize(S)
]
stringLiteralByteSlice(#S, #n, #m);
To extract a substring at compile time, use stringLiteralByteSlice. It returns the bytes of S from index n up to but not including m.
stringLiteralByteIndex
[S, n when StringLiteral?(S) and n >= 0 and n < stringLiteralByteSize(S)]
stringLiteralByteIndex(#S, #n) : Int32;
The byte at position n of S is available at compile time as an Int32.
stringLiteralBytes
[S when StringLiteral?(S)]
stringLiteralBytes(#S) : ..Int32;
To iterate over the bytes of a static string at compile time, use stringLiteralBytes. It returns the contents of S as a multiple-value list of Int32 values, one per byte, in order.
stringLiteralFromBytes
[..bytes]
stringLiteralFromBytes(#..bytes); // static string
When you need to construct a static string from individual bytes at compile time, stringLiteralFromBytes assembles a list of static integer arguments (each in 0 .. 255) into a static string.
stringTableConstant
[S when StringLiteral?(S)]
stringTableConstant(#S) : Pointer[SizeT];
Static strings only exist at compile time. To access one from running code, use stringTableConstant. It returns a pointer into the program's string table, where the data is laid out as a SizeT length prefix followed by the string's bytes. In practice you won't call this directly — StringLiteralRef does it for you.
Type Introspection
TypeSize
[T when Type?(T)]
TypeSize(#T) : SizeT;
The size in bytes of a value of type T is available at compile time via TypeSize.
TypeAlignment
[T when Type?(T)]
TypeAlignment(#T) : SizeT;
The natural alignment of type T, in bytes, is available at compile time via TypeAlignment.
BaseType
[T when Type?(T)]
BaseType(#T); // static type
For a newtype, the underlying representation type is available via BaseType. For any other type, BaseType(T) is just T itself.
TupleElementCount
[..T]
TupleElementCount(#Tuple[..T]) : SizeT;
The number of elements in a tuple type is available at compile time via TupleElementCount.
UnionMemberCount
[..T]
UnionMemberCount(#Union[..T]) : SizeT;
The number of member types in a union is available at compile time via UnionMemberCount.
Record Introspection
[R]
Record?(#R) : Bool;
[R when Record?(R)]
RecordFieldCount(#R) : SizeT;
[R, n when Record?(R) and n >= 0 and n < RecordFieldCount(R)]
RecordFieldName(#R, #n); // static string
[R, name when Record?(R) and StringLiteral?(name)]
RecordWithField?(#R, #name) : Bool;
Record?:trueifRnames a record type.RecordFieldCount: the number of fields in record typeR.RecordFieldName: the name of thenth field as a static string.RecordWithField?:trueifRhas a field namedname.
Variant Introspection
[V]
Variant?(#V) : Bool;
[V when Variant?(V)]
VariantMemberCount(#V) : SizeT;
[V, M when Variant?(V)]
VariantMemberIndex(#V, #M) : SizeT;
[V when Variant?(V)]
VariantMembers(#V); // static types
Variant?:trueifVnames a variant type.VariantMemberCount: the number of instance types.VariantMemberIndex: the ordinal index of instance typeMwithinV. Each instance maps to a distinct index in0 .. VariantMemberCount(V); the mapping is unspecified but stable.VariantMembers: a multiple-value list of the instance types ofV, in index order.
Enum Introspection
[E]
Enum?(#E) : Bool;
[E when Enum?(E)]
EnumMemberCount(#E) : SizeT;
[E, n when Enum?(E) and n >= 0 and n < EnumMemberCount(E)]
EnumMemberName(#E, #n); // static string
Enum?:trueifEnames an enum type.EnumMemberCount: the number of values.EnumMemberName: the name of thenth value as a static string.
Lambda Introspection
Compile-time predicates over lambda types. A lambda record is the anonymous record type created when a lambda expression captures variables. A lambda symbol is a non-capturing lambda equivalent to a named function.
[F]
LambdaRecord?(#F) : Bool;
[F]
LambdaSymbol?(#F) : Bool;
[F]
LambdaMono?(#F) : Bool;
[F when LambdaMono?(F)]
LambdaMonoInputTypes(#F); // static types
LambdaRecord?:trueifFis the type of a capturing lambda.LambdaSymbol?:trueifFis a procedure symbol created from a non-capturing lambda.LambdaMono?:trueif the lambda record typeFis monomorphic (its single overload has no pattern variables).LambdaMonoInputTypes: the argument types of the monomorphic overload of lambda record typeF, as a multiple-value list.