> For the complete documentation index, see [llms.txt](https://learn.fi-code.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://learn.fi-code.com/overview/maps-and-lists.md).

# 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**](/overview/variable-modifiers.md)**.**

```
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**](/overview/variable-modifiers.md)**.**

```
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**](/overview/variable-modifiers.md)**.**

```
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
}
```
