Skip to content

Like Types

bearshape has two broad input-contract families:

  • Like types such as F32Like[N, C]
  • ScalarLike types such as U8ScalarLike

Use them when your API accepts values that will be converted, normalized, or range-checked before real work begins.

Basic usage

Like types accept scalars, backend arrays, NumPy arrays, and nested sequences. They must always be subscripted.

import numpy as np
from beartype import beartype
from bearshape import N, C
from bearshape.numpy import F32, F32Like

@beartype
def to_array(x: F32Like[...]) -> np.ndarray:
  return np.asarray(x, dtype=np.float32)

to_array(3.14)  # scalar
to_array([1.0, 2.0, 3.0])  # 1D list
to_array([[1.0, 2.0], [3.0, 4.0]])  # 2D nested list
to_array(np.ones((3, 4)))  # ndarray
to_array([[[[[[1.0]]]]]])  # arbitrarily deep nesting

@beartype
def process(x: F32Like[N, C]) -> F32[N, C]:
  return np.asarray(x, dtype=np.float32)

Use [...] to mean "any rank" or a full dimension spec when the input shape itself matters.

All built-in Like aliases are created with make_array_like_type(..., casting="same_kind"), so dtype compatibility follows NumPy's "same_kind" casting rules by default. For example, int32 can flow into F32Like[...], but complex128 cannot.

Built-in Like aliases

NumPy exports the broadest set:

  • concrete aliases such as BoolLike, I8Like, F32Like, C128Like
  • category aliases such as IntLike, FloatLike, NumLike, ShapedLike

JAX, PyTorch, and CuPy export parallel Like families with backend-specific conversion behavior.

That default matters in practice:

  • F32Like[...] accepts integer inputs that can be cast to float32 under "same_kind"
  • F32Like[...] rejects complex inputs, because complex-to-float is not "same_kind"
  • IntLike[...] accepts integer-like inputs but not floating-point values under the same rule

ScalarLike types (range-validated scalars)

ScalarLike types validate individual scalar values. There is no shape component, only value- and dtype-family constraints.

from beartype import beartype
from bearshape.numpy import U8ScalarLike

@beartype
def clamp_pixel(value: U8ScalarLike) -> int:
  return int(value)

clamp_pixel(128)  # OK
clamp_pixel(256)  # Raises
clamp_pixel(-1)  # Raises

!!! warning "Boolean exclusion" Numeric scalar aliases (I8ScalarLike,

F32ScalarLike, NumScalarLike, etc.) reject bool and np.bool_ values. Python bool is a subclass of int, but bearshape treats booleans as non-numeric. Use BoolScalarLike for boolean scalars.

Available families include:

  • concrete aliases: BoolScalarLike, I8ScalarLike through I64ScalarLike, U8ScalarLike through U64ScalarLike, F16ScalarLike through F128ScalarLike, C64ScalarLike, C128ScalarLike, C256ScalarLike
  • category aliases: IntScalarLike, FloatScalarLike, RealScalarLike, NumScalarLike, ShapedScalarLike, and others
  • StringLike for str | np.str_

Backend modules re-export these NumPy-defined scalar types:

from bearshape.numpy import U8ScalarLike
from bearshape.jax import U8ScalarLike
from bearshape.torch import U8ScalarLike
from bearshape.cupy import U8ScalarLike

!!! note Backend-native 0-D arrays such as jnp.array(1.0) or

torch.tensor(1.0) are not ScalarLike. Use a Like alias with Scalar, for example F32Like[Scalar].

Backend-specific conversion behavior

The Like family is intentionally backend-aware:

  • bearshape.numpy slow-path conversion uses np.asarray
  • bearshape.jax slow-path conversion uses jnp.asarray, so objects implementing __jax_array__ are accepted
  • bearshape.torch slow-path conversion uses torch.as_tensor
  • bearshape.cupy slow-path conversion uses cupy.asarray

Static type checkers only see the backend array type, not the broader runtime acceptance of scalars and nested sequences.

Custom ScalarLike types

Use make_scalar_like_type when the built-in scalar families are close but not quite right for your API:

import numpy as np
from bearshape.numpy import make_scalar_like_type

F32ScalarDefault = make_scalar_like_type(np.float32)  # same_kind
F32ScalarStrict = make_scalar_like_type(np.float32, casting="no")
F32ScalarSafe = make_scalar_like_type(np.float32, casting="safe")
F32ScalarUnsafe = make_scalar_like_type(np.float32, casting="unsafe")

Useful interpretations:

  • casting="no" means "exact target dtype only"
  • casting="safe" means "no information loss"
  • casting="same_kind" means "same numeric family" and is the default
  • casting="unsafe" is the most permissive option and should be used deliberately

target_dtype may be passed as a NumPy scalar type, a dtype object, or a canonical dtype string such as "float32".

Numeric ScalarLike factories still reject booleans by design:

U8Scalar = make_scalar_like_type(np.uint8)
BoolScalar = make_scalar_like_type(np.bool_)

# U8Scalar rejects True / False
# BoolScalar accepts True / False

make_scalar_like_type is intentionally documented on backend modules such as bearshape.numpy; it is not part of the lightweight root bearshape import surface.

Casting rules

Both make_array_like_type and make_scalar_like_type use NumPy casting semantics:

| Casting | Meaning | Example for target float32 | | ------------- | --------------------- | ---------------------------- | | "no" | Exact dtype only | only float32 | | "equiv" | Same kind and size | float32 but not float64 | | "safe" | No information loss | int16 yes, float64 no | | "same_kind" | Same-kind conversion | int32 yes, complex64 no | | "unsafe" | Any cast NumPy allows | very permissive |

Default used by built-in Like aliases

The built-in aliases such as F32Like, I64Like, IntLike, FloatLike, and NumLike all use:

casting="same_kind"

That is the library default for make_array_like_type(...), and it is the behavior you get unless you build a custom alias yourself.

If you need stricter or looser input acceptance, make a custom alias instead of relying on the built-ins:

from bearshape import make_array_like_type
from bearshape._dtypes import FLOAT32

F32Exact = make_array_like_type(FLOAT32, casting="no", name="F32Exact")
F32Unsafe = make_array_like_type(FLOAT32, casting="unsafe", name="F32Unsafe")

ArrayLike template

bearshape.numpy.ArrayLike is a public recursive type alias for custom static typing combinations:

import numpy as np
from bearshape.numpy import ArrayLike

type MyInputType = ArrayLike[float, np.float32]

That template is most useful when you want your own checker-friendly alias but still follow bearshape's "scalar or nested sequence or array" model.