Skip to content
Back to top
View this page

# Types and Values

The Viper type system provides strong typing with runtime validation. This chapter covers all available types and how to create values.

## The Universal Constructor

Create any value using `Value.create(type, [initial_value])`:

    >>> from dsviper import *

    >>> Value.create(TypeFloat())
    0.0

    >>> Value.create(Type.STRING, "hello")
    'hello'

    >>> Value.create(TypeVector(Type.INT64), [1, 2, 3])
    [1, 2, 3]

If `initial_value` is omitted, the value is initialized to the type’s “zero”.

## Choosing the Right Constructor

Viper offers multiple ways to create values. Use this guide to pick the right one:

Pattern | When to Use | Example
--- | --- | ---
`Type.STRING` | Primitive type constant | `Value.create(Type.STRING, "hello")`
`TypeVector(...)` | Parameterized container | `Value.create(TypeVector(Type.INT64), [1,2,3])`
`ValueString("...")` | Direct value (known type) | `ValueString("hello")`
`Value.create(t, v)` | Universal factory | `Value.create(t_struct, {"field": 1})`
`Value.deduce(v)` | Let Viper infer type | `Value.deduce([1, 2, 3])`
`ValueUUId.create()` | Factory with generation | `ValueUUId.create()`

Quick rules:

- Use Type.X for primitives: `Type.STRING`, `Type.INT64`, `Type.BOOL`

- Use TypeX(…) for containers: `TypeVector(...)`, `TypeMap(...)`, `TypeOptional(...)`

- Use ValueX(…) when type is known and you want direct construction

- Use Value.create(t, v) when working with dynamic types or DSM definitions

- Use Value.deduce(v) for quick prototyping (type inferred from Python value)

## Type Constants

Common types are available as constants on `Type`:

Constant | Type
--- | ---
`Type.BOOL` | Boolean
`Type.INT8` to `Type.INT64` | Signed integers
`Type.UINT8` to `Type.UINT64` | Unsigned integers
`Type.FLOAT` | 32-bit float
`Type.DOUBLE` | 64-bit double
`Type.STRING` | UTF-8 string
`Type.UUID` | UUID
`Type.ANY` | Any type

## Primitive Types

### Boolean

Note that Viper values render with their own `repr` — booleans appear as `true`/`false`, not Python’s `True`/`False`.

    >>> Value.create(Type.BOOL)
    false

    >>> Value.create(Type.BOOL, True)
    true

Type checking is enforced:

    >>> Value.create(Type.BOOL, 4)
    Traceback (most recent call last):
        ...
    dsviper.ViperError: ...expected type 'bool', got 'int'...

### Integers

Signed and unsigned integers with range validation:

    >>> Value.create(Type.INT8)
    0

    >>> Value.create(Type.INT8, 42)
    42

    >>> Value.create(Type.INT8, 256)
    Traceback (most recent call last):
        ...
    dsviper.ViperError: ...value is not in the range of 'int8'...

Available types: `INT8`, `INT16`, `INT32`, `INT64`, `UINT8`, `UINT16`, `UINT32`, `UINT64`

### Floats

    >>> Value.create(Type.FLOAT)
    0.0

    >>> Value.create(Type.FLOAT, 42)
    42.0

    >>> Value.create(Type.FLOAT, 1e300)
    Traceback (most recent call last):
        ...
    dsviper.ViperError: ...value is not in the range of 'float'...

### String

UTF-8 encoded strings:

    >>> Value.create(Type.STRING)
    ''

    >>> Value.create(Type.STRING, "hello world")
    'hello world'

### UUID

Generate a fresh UUID — `ValueUUId.create()` returns a randomly-generated value:

    >>> uuid = ValueUUId.create()
    >>> isinstance(str(uuid), str) and str(uuid).count('-') == 4
    True

Parse a known UUID string:

    >>> uuid = ValueUUId.create("c98ddeec-0496-494c-b1e9-b470ff204be3")
    >>> uuid
    c98ddeec-0496-494c-b1e9-b470ff204be3

Malformed UUIDs are rejected:

    >>> ValueUUId.create("invalid")
    Traceback (most recent call last):
        ...
    dsviper.ViperError: ...malformed UUId...

## Mathematical Types

### Vec

Fixed-size numeric arrays:

    >>> t = TypeVec(Type.FLOAT, 3)
    >>> Value.create(t)
    (0.0, 0.0, 0.0)

    >>> Value.create(t, (1, 2, 3))
    (1.0, 2.0, 3.0)

### Mat

Matrices with columns and rows:

    >>> t = TypeMat(Type.FLOAT, 2, 3)
    >>> Value.create(t)
    [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]

    >>> Value.create(t, ((1, 2, 3), (4, 5, 6)))
    [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]

## Type Deduction

Let Python infer the Viper type with `Value.deduce()`:

    >>> v = Value.deduce([1, 2, 3])
    >>> v.type()
    vector<int64>

    >>> v = Value.deduce([1, 2, (3, "string"), {1.0, 2.0}])
    >>> v.type()
    vector<int64|set<double>|tuple<int64, string>>

## Type Information

Every value knows its type:

    >>> v = Value.create(TypeVector(Type.STRING), ["a", "b"])
    >>> v.type()
    vector<string>

    >>> v.description()
    "['a', 'b']:vector<string>"

## Type Checking

Viper validates types at runtime:

    >>> v = Value.create(TypeVector(Type.STRING))
    >>> v.append("valid")
    >>> v.append(123)
    Traceback (most recent call last):
        ...
    dsviper.ViperError: ...expected type 'str', got 'int'...

## The Seamless Bridge

Python natives are automatically converted:

    >>> v = Value.create(TypeVector(Type.STRING))
    >>> v.extend(["from", "python", "list"])
    >>> v
    ['from', 'python', 'list']

    >>> m = Value.create(TypeMap(Type.STRING, Type.INT64), {"a": 1, "b": 2})
    >>> m
    {'a': 1, 'b': 2}

The bridge uses type metadata to validate and convert Python objects.

## What’s Next

- Collections - Vectors, Maps, Sets, and more

- Structures and Enumerations - User-defined types