Capy-Bearer Documentation

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

42

State 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 11

Both 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.5

A 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

Ada

An 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

Ada

A 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 4

Copy 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 false

none 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:

ExpressionResultMeaning
value::sizes64The number of declared fields. This is not a byte size.
value::itemsanyA 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 Grace

parameter::type is a compile-time type expression. It does not inspect a runtime value. The compiler rejects the removed type(parameter) spelling.