3. Types
Every Capy value has a type. The compiler checks types before the program runs. It usually infers the type of a local value.
Integers
An integer literal has type s64 when no other type is stated:
function RENDER()
{
var answer := 42
print(answer, "\n")
}
Output
42State the type on the local, or construct the value explicitly:
function RENDER()
{
var signed : s64 = 8
var inferred_signed := s64(9)
var unsigned : u64 = 10
var inferred_unsigned := u64(11)
print(signed, " ", inferred_signed, " ", unsigned, " ", inferred_unsigned, "\n")
}
Output
8 9 10 11Both forms produce the stated integer type. var signed : s64 = s64(8) also compiles, but the constructor repeats the annotation.
Parameters, function results, assignments, typed arrays, struct fields, and the other operand in an expression can also state the integer type. Thus, a function that accepts u64 can receive the plain literal 8. An unconstrained literal remains s64.
Arithmetic requires operands of the same type. Use a constructor when the surrounding code does not state the required type.
Booleans and floating-point numbers
A bool is either true or false. Conditions require a boolean value.
function RENDER()
{
var enabled := true
var ratio := 2.5
print(enabled, " ", ratio, "\n")
}
Output
true 2.5A decimal point or exponent makes an f64 literal.
Strings
A string stores bytes:
function RENDER()
{
var name := "Ada"
var empty := ""
print(name, empty, "\n")
}
Output
AdaAn empty string is still a string value. It is not missing data.
Arrays and structs
An array contains values of one type:
function RENDER()
{
var names := ["Ada", "Grace"]
print(names[0], "\n")
}
Output
AdaA struct gives names to fixed fields:
struct Point
{
x : s32
y : s32
}
function RENDER()
{
var point := Point(3, 4)
print(point.x, " ", point.y, "\n")
}
Output
3 4Copy or share
Numbers and booleans are copied: after var b := a, changing b leaves a alone. Strings are values too. A string never changes in place, so it makes no difference whether two names share one.
Arrays, structs, and dvals are objects. var b := a makes b a second name for the same object, and so do parameters, return values, closure captures, and member reads. A change made through one name shows through all of them. When you need a separate copy, clone(value) makes one.
A variable can be pointed at another object with =. A parameter cannot.
The collections chapter covers arrays and structs in detail, the dynamic values chapter covers any.
Numeric functions
Use the math functions for floating-point calculations and numeric bounds. Use the bits functions for integer masks, shifts, rotations, counts, and bit reinterpretation.
See the math function reference for floating-point operations and predicates. See the bits function reference for integer bit operations and floating-point bit reinterpretation.
Dynamic values and none
An any holds data whose shape is known only at run time. The none value represents missing dynamic data:
function RENDER()
{
var profile := { name: "Ada" }
var missing := none
print(profile.name, " ", missing?, "\n")
}
Output
Ada falsenone is different from "", false, an empty map, and an empty list. An any is also an object that variables refer to, never a copy. The dynamic-values chapter explains mutation, safe reads, and conversions.
Type aliases and static type names
A type alias gives another name to a type. It does not create a distinct type at runtime.
Use expression::type_name to get the resolved static type of an expression. Capy evaluates the expression once.
type Count = s64
function RENDER()
{
var total : Count = 12
var labels := ["new", "done"]
print(total::type_name, " ", labels::type_name, "\n")
}
Output
s64 [string]An alias reports its target type. Arrays use [item_type]. Structs use their declared name. Function values use their complete function type.
An any expression reports any, regardless of its current contents. Use get_type_name(value) to inspect the value stored inside an any.
Struct field inspection
Struct values have two additional reflection expressions:
| Expression | Result | Meaning |
|---|---|---|
value::size | s64 | The number of declared fields. This is not a byte size. |
value::items | any | A new map of the current field values, keyed by field name. |
Each items entry contains value and type_name. Use fields.name.value for a fixed field. Use fields[key]["value"] for a dynamic field name.
value::items evaluates its receiver once. Changing the returned map does not change the struct. The compiler rejects size and items on non-struct values.
The struct reference defines field conversions for scalars, strings, DValues, nested structs, arrays, and function values.
Dependent generic types
A generic function can use the concrete type of an earlier * parameter. Write parameter::type in a later parameter or the function result:
function same(first : *, second : first::type) first::type
{
-> second
}
function RENDER()
{
print(same(1, 2), " ", same("Ada", "Grace"), "\n")
}Output
2 Graceparameter::type is a compile-time type expression. It does not inspect a runtime value. The compiler rejects the removed type(parameter) spelling.