3 dtype-next background
dtype-next is a Clojure library for working with typed numerical data. La Linea builds on its tensor abstraction — this chapter introduces the key ideas you need to know.
(ns lalinea-book.dtype-next-background
(:require [tech.v3.tensor :as dtt]
[tech.v3.datatype :as dtype]
[tech.v3.datatype.functional :as dfn]
[scicloj.kindly.v4.kind :as kind]))Buffers and tensors
dtype-next’s core abstraction is the buffer — a typed, indexed container backed by a Java array, off-heap memory, or a functional reader. A tensor is a multi-dimensional view over a buffer. Reshaping or slicing a tensor creates a new view without copying data — the same backing storage is indexed differently.
(def t (dtt/->tensor [[1 2 3]
[4 5 6]] {:datatype :float64}))(dtype/shape t)[2 3]Tensors are callable — (t i j) reads element [i,j]:
[(t 0 1) (t 1 0)][2.0 4.0]Lazy and noncaching
dtype-next follows a lazy, noncaching philosophy. Element-wise operations return lightweight readers that re-evaluate on every access rather than storing results:
(let [a (dtt/->tensor [1 2 3] {:datatype :float64})
b (dtt/->tensor [10 20 30] {:datatype :float64})
s (dfn/+ a b)]
{:values (vec s)
:has-array? (some? (dtype/as-array-buffer s))}){:values [11.0 22.0 33.0], :has-array? false}The result s has no backing array — it recomputes on every read. This avoids intermediate allocations and lets you compose long pipelines with near-zero overhead.
The trade-off: reading the same element twice computes it twice. When you need a concrete array, call dtype/clone (or t/materialize in La Linea, which is a no-op if already concrete):
(let [s (dfn/+ (dtt/->tensor [1 2 3] {:datatype :float64})
(dtt/->tensor [10 20 30] {:datatype :float64}))
materialized (dtype/clone s)]
(some? (dtype/as-array-buffer materialized)))trueWhy La Linea builds on dtype-next
dtype-next provides high-performance typed buffers and tensors for Clojure. Tablecloth/tech.ml.dataset and other libraries in the Clojure data science ecosystem build on it. La Linea uses dtype-next as its tensor layer so that matrices interoperate with that ecosystem.
A La Linea matrix is backed by a dtype-next tensor. It implements dtype-next’s protocols, so dtype/clone, dtype/shape, and dfn/ reductions work on it directly. The Ecosystem Composability chapter explores this interop in detail.
For the computational backend, La Linea uses EJML. This pairing works naturally: dtype-next tensors and EJML’s DMatrixRMaj share the same memory layout (a flat double[] in row-major order), enabling zero-copy interop:
| Type | Backing | Layout |
|---|---|---|
dtype-next tensor [r c] |
double[r*c] |
row-major |
EJML DMatrixRMaj |
double[r*c] |
row-major |
La Linea inherits dtype-next’s lazy philosophy: element-wise operations (el/+, el/-, el/*) return lazy readers, while operations that cross into EJML (la/mmul, la/solve, la/invert) materialize at the boundary. You get lazy composition by default, with explicit materialization only where it matters.
The next chapter introduces La Linea’s tensor API.