# Complex Types

As well as the basic types, fi also allows the following complex types which usually work in conjunction with other types. Complex types can't be used in a literal way and have specific constructors for creating new variables.

## Maps

Maps require a key and an element, and can form a large list of mapped elements. Keys must be unique for each entry, and can be used to retrieve a specific entry for a map.

A map key must be one of the following types:

* string
* bool
* int
* nat
* bytes
* mutez
* timestamp
* address
* key\_hash/pkh

We define the map type in the following way:

```
storage map[address => nat] balances;
```

This creates a map type variable, with a map key of type address, and a map item of type nat, stored as storage.balances.

### Empty Map

An empty map can be defined using the following:

```
let map[int=>string] passages = new map(int, string);
```

### Operations

The following operations can be used with maps

* **in(map, map\_key)** - returns bool if key exists as a map key in the map
* **length(map)** - returns cardinal size of the map as a **nat**
* **get(map, map\_key)** - returns item with index key. Fails if key doesn't exist (it is advised to use in first)
* **push(map, map\_key, map\_item)** - no return. Pushes item into map at index key. Will add the element if key doesn't exist, and update if it does (acts as an upsert)
* **drop(map, map\_key)** - no return. Removes the key/element pair from the map&#x20;

**These functions can be used as standalone functions, or as a** [**variable modifier**](https://learn.fi-code.com/overview/variable-modifiers)**.**

```
let map[int => string] textMap = new map(int, string);
push(textMap, int 1, "One");
textMap.push(int 2, "One"); // Same as above
```

## Big Map

Big maps work the same as normal maps, but have a few restrictions:

1. Big maps can only be declared as a storage variable
2. Only one big\_map can exist for any given contract
3. The length() function doesn't work with big\_maps
4. Big maps can't be constructed

We define the map type in the following way:

```
storage bmap[address => nat] balances;
```

All functions, other then length, are applicable for big maps.

## Lists

Lists are non-indexed arrays of a specified type, and are defined as follows:

```
let string[] names;
```

This creates a list of the specified type - in this instance a list of strings, which can be accessed via storage.names.

### Empty list

An empty map can be defined using the following:

```
let string[] names = new list(string);
```

### Operations

The following operations are available for lists:

* **length(list)** - returns cardinal size of a list as a nat
* **push(list, item)** - pushes an item to a list
* **pop(list)** - removes the last item from a list and returns it

**These functions can be used as standalone functions, or as a** [**variable modifier**](https://learn.fi-code.com/overview/variable-modifiers)**.**

```
let string[] names = new list(string);
push(names, "John");
names.push("Bill"); // Same as above
```

## Set

Sets are similar to lists as we store a single type of data per entry, and can be declared as follows:

```
storage set[nat] numbers;
```

The set item type must be one of the following types (same as the map key):

* string
* bool
* int
* nat
* bytes
* mutez
* timestamp
* address
* key\_hash/pkh

### Operations

The following operations are available for lists:

* **length(set)** - returns cardinal size of a set as a **nat**
* **push(set, item)** - pushes an item to a set
* **in(set, item)** - returns bool if item exists in the set
* **drop(set, item)** - no return. Removes the item from a set&#x20;

**These functions can be used as standalone functions, or as a** [**variable modifier**](https://learn.fi-code.com/overview/variable-modifiers)**.**

```
let set[string] names = new set(string);
push(names, "John");
names.push("Bill");
```

## Contract

The contract type is the typed version of an address. Michelson contract types must also specify the type of the input parameter.

```
let contract[unit] implicit = to_contract(address "tz1...");
```

## Option

Optional values can be set and then used within expressions. These values either hold "some" value, or "none". Optional values are defined by adding a question mark before another type:

```
let ?nat optional = to_optional(nat 100);
```

We can convert any existing value to an optional value using the **to\_optional** cast, or we can use the **none** function to create an optional variable with no value.

```
let ?bool test = none(bool);
if (isset(test) == false) {
    #This will execute
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://learn.fi-code.com/overview/maps-and-lists.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
