# Primitives

Gel has a robust type system consisting of primitive and object types. types. Primitive types are used to declare *properties* on object types, as query and function arguments, as as well as in other contexts.

## Built-in scalar types

Gel comes with a range of built-in scalar types, such as:

- String: [`str`](https://docs.geldata.com/reference/stdlib/string.md#type::std::str)
- Boolean: [`bool`](https://docs.geldata.com/reference/stdlib/bool.md#type::std::bool)
- Various numeric types: [`int16`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::int16), [`int32`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::int32), [`int64`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::int64), [`float32`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::float32), [`float64`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::float64), [`bigint`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::bigint), [`decimal`](https://docs.geldata.com/reference/stdlib/numbers.md#type::std::decimal)

- JSON: [`json`](https://docs.geldata.com/reference/stdlib/json.md#type::std::json),
- UUID: [`uuid`](https://docs.geldata.com/reference/stdlib/uuid.md#type::std::uuid),
- Date/time: [`datetime`](https://docs.geldata.com/reference/stdlib/datetime.md#type::std::datetime), [`duration`](https://docs.geldata.com/reference/stdlib/datetime.md#type::std::duration) [`cal::local_datetime`](https://docs.geldata.com/reference/stdlib/datetime.md#type::cal::local_datetime), [`cal::local_date`](https://docs.geldata.com/reference/stdlib/datetime.md#type::cal::local_date), [`cal::local_time`](https://docs.geldata.com/reference/stdlib/datetime.md#type::cal::local_time), [`cal::relative_duration`](https://docs.geldata.com/reference/stdlib/datetime.md#type::cal::relative_duration), [`cal::date_duration`](https://docs.geldata.com/reference/stdlib/datetime.md#type::cal::date_duration)

- Miscellaneous: [`sequence`](https://docs.geldata.com/reference/stdlib/sequence.md#type::std::sequence), [`bytes`](https://docs.geldata.com/reference/stdlib/bytes.md#type::std::bytes), etc.


## Custom scalars

You can extend built-in scalars with additional constraints or annotations. Here's an example of a non-negative custom `int64` variant:

```sdl
scalar type posint64 extending int64 {
    constraint min_value(0);
}
```

## Enums

Enum types are created by extending the abstract [`enum`](https://docs.geldata.com/reference/stdlib/enum.md#type::std::enum) type, e.g.:

```sdl
scalar type Color extending enum<Red, Green, Blue>;

type Shirt {
  color: Color;
}
```

which can be queries with:

```edgeql
select Shirt filter .color = Color.Red;
```

For a full reference on enum types, see the [Enum docs](https://docs.geldata.com/reference/stdlib/enum.md#ref-std-enum).

## Arrays

Arrays store zero or more primitive values of the same type in an ordered list. Arrays cannot contain object types or other arrays, but can contain virtually any other type.

```sdl
type Person {
  str_array: array<str>;
  json_array: array<json>;
  tuple_array: array<tuple<float32, float32>>;

  # INVALID: arrays of object types not allowed:
  # friends: array<Person>

  # INVALID: arrays cannot be nested:
  # nested_array: array<array<str>>

  # VALID: arrays can contain tuples with arrays in them
  nested_array_via_tuple: array<tuple<array<str>>>
}
```

Array syntax in EdgeQL is very intuitive (indexing starts at `0`):

```edgeql
select [1, 2, 3];
select [1, 2, 3][1] = 2;  # true
```

For a full reference on array types, see the [Array docs](https://docs.geldata.com/reference/stdlib/array.md#ref-std-array).

## Tuples

Like arrays, tuples are ordered sequences of primitive data. Unlike arrays, each element of a tuple can have a distinct type. Tuple elements can be *any type*, including primitives, objects, arrays, and other tuples.

```sdl
type Person {
  unnamed_tuple: tuple<str, bool, int64>;
  nested_tuple: tuple<tuple<str, tuple<bool, int64>>>;
  tuple_of_arrays: tuple<array<str>, array<int64>>;
}
```

Optionally, you can assign a *key* to each element of the tuple. Tuples containing explicit keys are known as *named tuples*. You must assign keys to all elements (or none of them).

```sdl
type BlogPost {
  metadata: tuple<title: str, published: bool, upvotes: int64>;
}
```

Named and unnamed tuples are the same data structure under the hood. You can add, remove, and change keys in a tuple type after it's been declared. For details, see [Tuples](https://docs.geldata.com/reference/edgeql/literals.md#ref-eql-literal-tuple).

> Note: When you query an *unnamed* tuple using one of EdgeQL's [client libraries](https://docs.geldata.com/reference/using/clients.md#ref-clients-index), its value is converted to a list/array. When you fetch a named tuple, it is converted into an object/dictionary/hashmap depending on the language.

## Ranges

Ranges represent some interval of values. The intervals can be bound or unbound on either end. They can also be empty, containing no values. Only some scalar types have corresponding range types:

- Numeric ranges: `range<int32>`, `range<int64>`, `range<float32>`, `range<float64>`, `range<decimal>`
- Date/time ranges: `range<datetime>`, `range<cal::local_datetime>`, `range<cal::local_date>`

Example:

```sdl
type DieRoll {
  values: range<int64>;
}
```

For a full reference on ranges, functions and operators see the [Range docs](https://docs.geldata.com/reference/stdlib/range.md#ref-std-range).

## Sequences

To represent an auto-incrementing integer property, declare a custom scalar that extends the abstract `sequence` type. Creating a sequence type initializes a global `int64` counter that auto-increments whenever a new object is created. All properties that point to the same sequence type will share the counter.

```sdl
scalar type ticket_number extending sequence;
type Ticket {
  number: ticket_number;
  rendered_number := 'TICKET-\(.number)';
}
```

For a full reference on sequences, see the [Sequence docs](https://docs.geldata.com/reference/stdlib/sequence.md#ref-std-sequence).

## Declaring scalars

This section describes the syntax to declare a custom scalar type in your schema.

### Syntax

```sdl-synopsis
[abstract] scalar type <TypeName> [extending <supertype> [, ...] ]
[ "{"
    [ <annotation-declarations> ]
    [ <constraint-declarations> ]
    ...
  "}" ]
```

#### Description

This declaration defines a new object type with the following options:

`abstract`:
If specified, the created scalar type will be *abstract*.



`<TypeName>`:
The name (optionally module-qualified) of the new scalar type.



`extending <supertype>`:
Optional clause specifying the *supertype* of the new type.

If `<supertype>` is an [`enumerated type`](https://docs.geldata.com/reference/stdlib/enum.md#type::std::enum) declaration then an enumerated scalar type is defined.

Use of `extending` creates a persistent type relationship between the new subtype and its supertype(s). Schema modifications to the supertype(s) propagate to the subtype.



The valid SDL sub-declarations are listed below:

`<annotation-declarations>`:
Set scalar type [annotation](https://docs.geldata.com/reference/datamodel/annotations.md#ref-eql-sdl-annotations) to a given *value*.



`<constraint-declarations>`:
Define a concrete [constraint](https://docs.geldata.com/reference/datamodel/constraints.md#ref-eql-sdl-constraints) for this scalar type.



## DDL commands

This section describes the low-level DDL commands for creating, altering, and dropping scalar types. You typically don't need to use these commands directly, but knowing about them is useful for reviewing migrations.

### Create scalar

Define a new scalar type.

```edgeql-synopsis
[ with <with-item> [, ...] ]
create [abstract] scalar type <name> [ extending <supertype> ]
[ "{" <subcommand>; [...] "}" ] ;

# where <subcommand> is one of

  create annotation <annotation-name> := <value>
  create constraint <constraint-name> ...
```

#### Description

The command `create scalar type` defines a new scalar type for use in the current branch.

If *name* is qualified with a module name, then the type is created in that module, otherwise it is created in the current module. The type name must be distinct from that of any existing schema item in the module.

If the `abstract` keyword is specified, the created type will be *abstract*.

All non-abstract scalar types must have an underlying core implementation. For user-defined scalar types this means that `create scalar type` must have another non-abstract scalar type as its *supertype*.

The most common use of `create scalar type` is to define a scalar subtype with constraints.

Most sub-commands and options of this command are identical to the [SDL scalar type declaration](https://docs.geldata.com/reference/datamodel/primitives.md#ref-eql-sdl-scalars-syntax). The following subcommands are allowed in the `create scalar type` block:

`create annotation <annotation-name> := <value>;`:
Set scalar type's `<annotation-name>` to `<value>`.

See [`create annotation`](https://docs.geldata.com/reference/datamodel/annotations.md#statement::create-annotation) for details.



`create constraint <constraint-name> ...`:
Define a new constraint for this scalar type. See [`create constraint`](https://docs.geldata.com/reference/datamodel/constraints.md#statement::create-constraint) for details.



#### Examples

Create a new non-negative integer type:

```edgeql
create scalar type posint64 extending int64 {
    create constraint min_value(0);
};
```

Create a new enumerated type:

```edgeql
create scalar type Color
    extending enum<Black, White, Red>;
```

### Alter scalar

Alter the definition of a scalar type.

```edgeql-synopsis
[ with <with-item> [, ...] ]
alter scalar type <name>
"{" <subcommand>; [...] "}" ;

# where <subcommand> is one of

  rename to <newname>
  extending ...
  create annotation <annotation-name> := <value>
  alter annotation <annotation-name> := <value>
  drop annotation <annotation-name>
  create constraint <constraint-name> ...
  alter constraint <constraint-name> ...
  drop constraint <constraint-name> ...
```

#### Description

The command `alter scalar type` changes the definition of a scalar type. *name* must be a name of an existing scalar type, optionally qualified with a module name.

The following subcommands are allowed in the `alter scalar type` block:

`rename to <newname>;`:
Change the name of the scalar type to *newname*.



`extending ...`:
Alter the supertype list. It works the same way as in [`alter type`](https://docs.geldata.com/reference/datamodel/objects.md#statement::alter-type).



`alter annotation <annotation-name>;`:
Alter scalar type `<annotation-name>`. See [`alter annotation`](https://docs.geldata.com/reference/datamodel/annotations.md#statement::alter-annotation) for details.



`drop annotation <annotation-name>`:
Remove scalar type's `<annotation-name>` from `<value>`. See [`drop annotation`](https://docs.geldata.com/reference/datamodel/annotations.md#statement::drop-annotation) for details.



`alter constraint <constraint-name> ...`:
Alter the definition of a constraint for this scalar type. See [`alter constraint`](https://docs.geldata.com/reference/datamodel/constraints.md#statement::alter-constraint) for details.



`drop constraint <constraint-name>`:
Remove a constraint from this scalar type. See [`drop constraint`](https://docs.geldata.com/reference/datamodel/constraints.md#statement::drop-constraint) for details.



All the subcommands allowed in the `create scalar type` block are also valid subcommands for `alter scalar type` block.

#### Examples

Define a new constraint on a scalar type:

```edgeql
alter scalar type posint64 {
    create constraint max_value(100);
};
```

Add one more label to an enumerated type:

```edgeql
alter scalar type Color
    extending enum<Black, White, Red, Green>;
```

### Drop scalar

Remove a scalar type.

```edgeql-synopsis
[ with <with-item> [, ...] ]
drop scalar type <name> ;
```

#### Description

The command `drop scalar type` removes a scalar type.

#### Parameters

*name*:
The name (optionally qualified with a module name) of an existing scalar type.



#### Example

Remove a scalar type:

```edgeql
drop scalar type posint64;
```

