% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/faq-internal.R
\name{internal-faq-ptype2-identity}
\alias{internal-faq-ptype2-identity}
\title{Internal FAQ - \code{vec_ptype2()}, \code{NULL}, and unspecified vectors}
\description{
\subsection{Promotion monoid}{

Promotions (i.e. automatic coercions) should always transform inputs to
their richer type to avoid losing values of precision. \code{vec_ptype2()}
returns the \emph{richer} type of two vectors, or throws an incompatible type
error if none of the two vector types include the other. For example,
the richer type of integer and double is the latter because double
covers a larger range of values than integer.

\code{vec_ptype2()} is a \href{https://en.wikipedia.org/wiki/Monoid}{monoid} over
vectors, which in practical terms means that it is a well behaved
operation for
\href{https://purrr.tidyverse.org/reference/reduce.html}{reduction}.
Reduction is an important operation for promotions because that is how
the richer type of multiple elements is computed. As a monoid,
\code{vec_ptype2()} needs an identity element, i.e. a value that doesn’t
change the result of the reduction. vctrs has two identity values,
\code{NULL} and \strong{unspecified} vectors.
}

\subsection{The \code{NULL} identity}{

As an identity element that shouldn’t influence the determination of the
common type of a set of vectors, \code{NULL} is promoted to any type:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype2(NULL, "")
#> character(0)
vec_ptype2(1L, NULL)
#> integer(0)
}\if{html}{\out{</div>}}

The common type of \code{NULL} and \code{NULL} is the identity \code{NULL}:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype2(NULL, NULL)
#> NULL
}\if{html}{\out{</div>}}

This way the result of \code{vec_ptype2(NULL, NULL)} does not influence
subsequent promotions:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype2(
  vec_ptype2(NULL, NULL),
  ""
)
#> character(0)
}\if{html}{\out{</div>}}
}

\subsection{Unspecified vectors}{

In the vctrs coercion system, logical vectors of missing values are also
automatically promoted to the type of any other vector, just like
\code{NULL}. We call these vectors unspecified. The special coercion
semantics of unspecified vectors serve two purposes:
\enumerate{
\item It makes it possible to assign vectors of \code{NA} inside any type of
vectors, even when they are not coercible with logical:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{x <- letters[1:5]
vec_assign(x, 1:2, c(NA, NA))
#> [1] NA  NA  "c" "d" "e"
}\if{html}{\out{</div>}}
\item We can’t put \code{NULL} in a data frame, so we need an identity element
that behaves more like a vector. Logical vectors of \code{NA} seem a
natural fit for this.
}

Unspecified vectors are thus promoted to any other type, just like
\code{NULL}:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype2(NA, "")
#> character(0)
vec_ptype2(1L, c(NA, NA))
#> integer(0)
}\if{html}{\out{</div>}}
}

\subsection{Finalising common types}{

vctrs has an internal vector type of class \code{vctrs_unspecified}. Users
normally don’t see such vectors in the wild, but they do come up when
taking the common type of an unspecified vector with another identity
value:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype2(NA, NA)
#> <unspecified> [0]
vec_ptype2(NA, NULL)
#> <unspecified> [0]
vec_ptype2(NULL, NA)
#> <unspecified> [0]
}\if{html}{\out{</div>}}

We can’t return \code{NA} here because \code{vec_ptype2()} normally returns empty
vectors. We also can’t return \code{NULL} because unspecified vectors need to
be recognised as logical vectors if they haven’t been promoted at the
end of the reduction.

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype_finalise(vec_ptype2(NULL, NA))
#> logical(0)
}\if{html}{\out{</div>}}

See the output of \code{vec_ptype_common()} which performs the reduction and
finalises the type, ready to be used by the caller:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype_common(NULL, NULL)
#> NULL
vec_ptype_common(NA, NULL)
#> logical(0)
}\if{html}{\out{</div>}}

\code{vec_ptype_finalise()} is an S3 generic, but the only time you should
ever need to write an S3 method for it is if your class \emph{wraps} another
vector in some way and needs special handling to propagate the default
finalisation. For example, the ivs package contains an interval class
that wraps \code{start} and \code{end} vectors of the same type and has a
\code{vec_ptype_finalize()} method that finalises those wrapped vectors:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{vec_ptype_finalise.ivs_iv <- function(x, ...) \{
  start <- unclass(x)[[1L]]
  ptype <- vec_ptype_finalise(start, ...)
  new_bare_iv(ptype, ptype)
\}
}\if{html}{\out{</div>}}

This ensures that \code{vec_ptype_finalise(vec_ptype(ivs::iv(NA, NA)))}
correctly finalises to \verb{<iv<logical>>} rather than \verb{<iv<unspecified>>}.

Note that data frames are already recursively finalised, so you don’t
need a \code{vec_ptype_finalise()} method for a data frame subclass.
}
}
