6.10 Type inference
Stan is strongly statically typed, meaning that the implementation type of an expression can be resolved at compile time.
Implementation types
The primitive implementation types for Stan are
int, real, complex, vector, row_vector, matrix, complex_vector,
complex_row_vector, complex_matrix
Every basic declared type corresponds to a primitive type; see the primitive type table for the mapping from types to their primitive types.
Primitive Type Table. The table shows the variable declaration types of Stan and their corresponding primitive implementation type. Stan functions, operators, and probability functions have argument and result types declared in terms of primitive types plus array dimensionality.
type | primitive type |
---|---|
int |
int |
real |
real |
vector |
vector |
simplex |
vector |
unit_vector |
vector |
ordered |
vector |
positive_ordered |
vector |
row_vector |
row_vector |
matrix |
matrix |
cov_matrix |
matrix |
corr_matrix |
matrix |
cholesky_factor_cov |
matrix |
cholesky_factor_corr |
matrix |
complex_vector |
complex_vector |
complex_row_vector |
complex_row_vector |
complex_matrix |
complex_matrix |
A full implementation type consists of a primitive implementation type
and an integer array dimensionality greater than or equal to zero.
These will be written to emphasize their array-like nature. For
example, array [] real
has an array dimensionality of 1, int
an
array dimensionality of 0, and array [,,] int
an array dimensionality
of 3. The implementation type matrix[ , , ]
has a total of five
dimensions and takes up to five indices, three from the array and two
from the matrix.
Recall that the array dimensions come before the matrix or vector dimensions in an expression such as the following declaration of a three-dimensional array of matrices.
array[I, J, K] matrix[M, N] a;
The matrix a
is indexed as a[i, j, k, m, n]
with the array
indices first, followed by the matrix indices, with a[i, j, k]
being a matrix and a[i, j, k, m]
being a row vector.
Type inference rules
Stan’s type inference rules define the implementation type of an expression based on a background set of variable declarations. The rules work bottom up from primitive literal and variable expressions to complex expressions.
6.10.1 Promotion
There are two basic promotion rules,
int
types may be promoted toreal
, andreal
types may be promoted tocomplex
.
Plus, promotion is transitive, so that
- if type
U
can be promoted to typeV
and typeV
can be promoted to typeT
, thenU
can be promoted toT
.
The first rule means that expressions of type int
may be used
anywhere an expression of type real
is specified, namely in
assignment or function argument passing. An integer is promoted to
real by casting it in the underlying C++ code.
The remaining rules have to do with covariant typing rules, which say
that a container of type U
may be promoted to a container of the
same shape of type T
if U
can be promoted to T
. For vector and
matrix types, this induces three rules,
vector
may be promoted tocomplex_vector
,row_vector
may be promoted tocomplex_row_vector
matrix
may be promoted tocomplex_matrix
.
For array types, there’s a single rule
array[...] U
may be promoted toarray[...] T
ifU
can be promoted toT
.
For example, this means array[,] int
may be used where array [,] real
or array [,] complex
is required; as another example, array[] real
may be used anywhere array[] complex
is required.
Literals
An integer literal expression such as 42
is of type int
.
Real literals such as 42.0
are of type real
. Imaginary literals
such as -17i
are of type complex
. the expression 7 - 2i
acts
like a complex literal, but technically it combines a real literal 7
and an imaginary literal 2i
through subtraction.
Variables
The type of a variable declared locally or in a previous block is
determined by its declaration. The type of a loop variable is
int
.
There is always a unique declaration for each variable in each scope because Stan prohibits the redeclaration of an already-declared variables.2
Indexing
If x
is an expression of total dimensionality greater than or equal
to \(N\), then the type of expression e[i1, i2, ..., iN]
is the same as
that of e[i1][i2]...[iN]
, so it suffices to define the type of a
singly-indexed function. Suppose e
is an expression and i
is an
expression of primitive type int
. Then
- if
e
is an expression of typearray[i1, i2, ..., iN] T
andk,
i1
, …,iN
are expressions of typeint
, thene[k]
is an expression of typearray[i2, ..., iN] T
, - if
e
is an expression of typearray[i] T
withi
andk
expressions of typeint
, thene[k]
is of typeT
, - if
e
has implementation typevector
orrow_vector
, dimensionality 0, thene[i]
has implementation typereal
, - if
e
has implementation typematrix
, thene[i]
has typerow_vector
, - if
e
has implementation typecomplex_vector
orcomplex_row_vector
andi
is an expression of typeint
, thene[i]
is an expression of typecomplex
, and - if
e
has implementation typecomplex_matrix
, andi
is an expression of typeint
, thene[i]
is an expression of typecomplex_row_vector
.
Function application
If f
is the name of a function and e1,...,eN
are
expressions for \(N \geq 0\), then f(e1,...,eN)
is an expression
whose type is determined by the return type in the function signature
for f
given e1
through eN
. Recall that a
function signature is a declaration of the argument types and the
result type.
In looking up functions, binary operators like real * real
are
defined as operator*(real, real)
in the documentation and index.
In matching a function definition, all of the promotion rules are in
play (integers may be promoted to reals, reals to complex, and
containers may be promoted if their types are promoted). For example,
arguments of type int
may be promoted to type real
or complex
if
necessary (see the subsection on type promotion in the function
application section, a real
argument will be
promoted to complex
if necessary, a vector
will be promoted to
complex_vector
if necessary, and so on.
In general, matrix operations return the lowest inferable type. For
example, row_vector * vector
returns a value of type
real
, which is declared in the function documentation and index
as real operator*(row_vector, vector)
.
Languages such as C++ and R allow the declaration of a variable of a given name in a narrower scope to hide (take precedence over for evaluation) a variable defined in a containing scope.↩︎