# Stored Types

> SmashTable for Python

Values are `int`, `float`, `bool`, `str` and `bytes`, deep-copied into memory the library owns, so a stored value outlives the object it came from.
`str` and `bytes` stay distinct, and `bool` round-trips as `bool` rather than as `1`.

A container may instead hold arbitrary objects, which it stores by reference rather than by copy:

```python
documents = st.SortedMap(key=str, value='object')
documents['doc'] = {'nested': [1, 2]}   # the very same object comes back out
```

The mode is named rather than inferred, because it is what the scalar guarantee rests on.
In the default scalar mode nothing stored can touch a reference count, so the interpreter lock is released around every store; in object mode it is held, and the container is correspondingly slower.

__Keys are typed and homogeneous.__
A container names its key layout at construction and refuses every other type:

```python
st.SortedMap(key=int)      # signed 64-bit
st.SortedMap(key='uint')   # unsigned 64-bit, the only way to ask, since Python has no unsigned type
st.SortedMap(key=str)      # UTF-8, ordered bytewise
st.SortedMap(key=bytes)    # opaque, never decoded
```

That is what lets every comparison skip type dispatch: the ordering function is chosen once, at construction, rather than re-derived from a tag on each of the millions of comparisons a tree makes.

`float` and `bool` are values but never keys.
A float key would make ordering depend on a total order over NaN, and `bool` is a subclass of `int` in Python, so accepting it would silently alias two key spaces:

```python
m = st.SortedMap(key=int)
m[1] = 'one'      # fine
m[1.0] = 'one'    # TypeError: float is not a valid key
m[True] = 'one'   # TypeError: bool is not a valid key
```

This is the one place the library is deliberately stricter than `dict`, which treats `1`, `1.0` and `True` as one key.

