1797 lines
148 KiB
HTML
Raw Normal View History

2025-01-12 00:52:51 +08:00
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta http-equiv="X-UA-Compatible" content="IE=EDGE" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>S3 vectors</title>
<script>// Pandoc 2.9 adds attributes on both header and div. We remove the former (to
// be compatible with the behavior of Pandoc < 2.8).
document.addEventListener('DOMContentLoaded', function(e) {
var hs = document.querySelectorAll("div.section[class*='level'] > :first-child");
var i, h, a;
for (i = 0; i < hs.length; i++) {
h = hs[i];
if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6
a = h.attributes;
while (a.length > 0) h.removeAttribute(a[0].name);
}
});
</script>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<style type="text/css">
code {
white-space: pre;
}
.sourceCode {
overflow: visible;
}
</style>
<style type="text/css" data-origin="pandoc">
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; }
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.at { color: #7d9029; }
code span.bn { color: #40a070; }
code span.bu { color: #008000; }
code span.cf { color: #007020; font-weight: bold; }
code span.ch { color: #4070a0; }
code span.cn { color: #880000; }
code span.co { color: #60a0b0; font-style: italic; }
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.do { color: #ba2121; font-style: italic; }
code span.dt { color: #902000; }
code span.dv { color: #40a070; }
code span.er { color: #ff0000; font-weight: bold; }
code span.ex { }
code span.fl { color: #40a070; }
code span.fu { color: #06287e; }
code span.im { color: #008000; font-weight: bold; }
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.kw { color: #007020; font-weight: bold; }
code span.op { color: #666666; }
code span.ot { color: #007020; }
code span.pp { color: #bc7a00; }
code span.sc { color: #4070a0; }
code span.ss { color: #bb6688; }
code span.st { color: #4070a0; }
code span.va { color: #19177c; }
code span.vs { color: #4070a0; }
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; }
</style>
<script>
// apply pandoc div.sourceCode style to pre.sourceCode instead
(function() {
var sheets = document.styleSheets;
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].ownerNode.dataset["origin"] !== "pandoc") continue;
try { var rules = sheets[i].cssRules; } catch (e) { continue; }
var j = 0;
while (j < rules.length) {
var rule = rules[j];
// check if there is a div.sourceCode rule
if (rule.type !== rule.STYLE_RULE || rule.selectorText !== "div.sourceCode") {
j++;
continue;
}
var style = rule.style.cssText;
// check if color or background-color is set
if (rule.style.color === '' && rule.style.backgroundColor === '') {
j++;
continue;
}
// replace div.sourceCode by a pre.sourceCode rule
sheets[i].deleteRule(j);
sheets[i].insertRule('pre.sourceCode{' + style + '}', j);
}
}
})();
</script>
<style type="text/css">body {
background-color: #fff;
margin: 1em auto;
max-width: 700px;
overflow: visible;
padding-left: 2em;
padding-right: 2em;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.35;
}
#TOC {
clear: both;
margin: 0 0 10px 10px;
padding: 4px;
width: 400px;
border: 1px solid #CCCCCC;
border-radius: 5px;
background-color: #f6f6f6;
font-size: 13px;
line-height: 1.3;
}
#TOC .toctitle {
font-weight: bold;
font-size: 15px;
margin-left: 5px;
}
#TOC ul {
padding-left: 40px;
margin-left: -1.5em;
margin-top: 5px;
margin-bottom: 5px;
}
#TOC ul ul {
margin-left: -2em;
}
#TOC li {
line-height: 16px;
}
table {
margin: 1em auto;
border-width: 1px;
border-color: #DDDDDD;
border-style: outset;
border-collapse: collapse;
}
table th {
border-width: 2px;
padding: 5px;
border-style: inset;
}
table td {
border-width: 1px;
border-style: inset;
line-height: 18px;
padding: 5px 5px;
}
table, table th, table td {
border-left-style: none;
border-right-style: none;
}
table thead, table tr.even {
background-color: #f7f7f7;
}
p {
margin: 0.5em 0;
}
blockquote {
background-color: #f6f6f6;
padding: 0.25em 0.75em;
}
hr {
border-style: solid;
border: none;
border-top: 1px solid #777;
margin: 28px 0;
}
dl {
margin-left: 0;
}
dl dd {
margin-bottom: 13px;
margin-left: 13px;
}
dl dt {
font-weight: bold;
}
ul {
margin-top: 0;
}
ul li {
list-style: circle outside;
}
ul ul {
margin-bottom: 0;
}
pre, code {
background-color: #f7f7f7;
border-radius: 3px;
color: #333;
white-space: pre-wrap;
}
pre {
border-radius: 3px;
margin: 5px 0px 10px 0px;
padding: 10px;
}
pre:not([class]) {
background-color: #f7f7f7;
}
code {
font-family: Consolas, Monaco, 'Courier New', monospace;
font-size: 85%;
}
p > code, li > code {
padding: 2px 0px;
}
div.figure {
text-align: center;
}
img {
background-color: #FFFFFF;
padding: 2px;
border: 1px solid #DDDDDD;
border-radius: 3px;
border: 1px solid #CCCCCC;
margin: 0 5px;
}
h1 {
margin-top: 0;
font-size: 35px;
line-height: 40px;
}
h2 {
border-bottom: 4px solid #f7f7f7;
padding-top: 10px;
padding-bottom: 2px;
font-size: 145%;
}
h3 {
border-bottom: 2px solid #f7f7f7;
padding-top: 10px;
font-size: 120%;
}
h4 {
border-bottom: 1px solid #f7f7f7;
margin-left: 8px;
font-size: 105%;
}
h5, h6 {
border-bottom: 1px solid #ccc;
font-size: 105%;
}
a {
color: #0033dd;
text-decoration: none;
}
a:hover {
color: #6666ff; }
a:visited {
color: #800080; }
a:visited:hover {
color: #BB00BB; }
a[href^="http:"] {
text-decoration: underline; }
a[href^="https:"] {
text-decoration: underline; }
code > span.kw { color: #555; font-weight: bold; }
code > span.dt { color: #902000; }
code > span.dv { color: #40a070; }
code > span.bn { color: #d14; }
code > span.fl { color: #d14; }
code > span.ch { color: #d14; }
code > span.st { color: #d14; }
code > span.co { color: #888888; font-style: italic; }
code > span.ot { color: #007020; }
code > span.al { color: #ff0000; font-weight: bold; }
code > span.fu { color: #900; font-weight: bold; }
code > span.er { color: #a61717; background-color: #e3d2d2; }
</style>
</head>
<body>
<h1 class="title toc-ignore">S3 vectors</h1>
<p>This vignette shows you how to create your own S3 vector classes. It
focuses on the aspects of making a vector class that every class needs
to worry about; youll also need to provide methods that actually make
the vector useful.</p>
<p>I assume that youre already familiar with the basic machinery of S3,
and the vocabulary I use in Advanced R: constructor, helper, and
validator. If not, I recommend reading at least the first two sections
of <a href="https://adv-r.hadley.nz/s3.html">the S3 chapter</a> of
<em>Advanced R</em>.</p>
<p>This article refers to “vectors of numbers” as <em>double
vectors</em>. Here, “double” stands for <a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">“double
precision floating point number”</a>, see also
<code>double()</code>.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb1-1"><a href="#cb1-1" tabindex="-1"></a><span class="fu">library</span>(vctrs)</span>
<span id="cb1-2"><a href="#cb1-2" tabindex="-1"></a><span class="fu">library</span>(rlang)</span>
<span id="cb1-3"><a href="#cb1-3" tabindex="-1"></a><span class="fu">library</span>(zeallot)</span></code></pre></div>
<p>This vignette works through five big topics:</p>
<ul>
<li>The basics of creating a new vector class with vctrs.</li>
<li>The coercion and casting system.</li>
<li>The record and list-of types.</li>
<li>Equality and comparison proxies.</li>
<li>Arithmetic operators.</li>
</ul>
<p>Theyre collectively demonstrated with a number of simple S3
classes:</p>
<ul>
<li><p>Percent: a double vector that prints as a percentage. This
illustrates the basic mechanics of class creation, coercion, and
casting.</p></li>
<li><p>Decimal: a double vector that always prints with a fixed number
of decimal places. This class has an attribute which needs a little
extra care in casts and coercions.</p></li>
<li><p>Cached sum: a double vector that caches the total sum in an
attribute. The attribute depends on the data, so needs extra
care.</p></li>
<li><p>Rational: a pair of integer vectors that defines a rational
number like <code>2 / 3</code>. This introduces you to the record style,
and to the equality and comparison operators. It also needs special
handling for <code>+</code>, <code>-</code>, and friends.</p></li>
<li><p>Polynomial: a list of integer vectors that define polynomials
like <code>1 + x - x^3</code>. Sorting such vectors correctly requires a
custom equality method.</p></li>
<li><p>Meter: a numeric vector with meter units. This is the simplest
possible class with interesting algebraic properties.</p></li>
<li><p>Period and frequency: a pair of classes represent a period, or
its inverse, frequency. This allows us to explore more arithmetic
operators.</p></li>
</ul>
<div id="basics" class="section level2">
<h2>Basics</h2>
<p>In this section youll learn how to create a new vctrs class by
calling <code>new_vctr()</code>. This creates an object with class
<code>vctrs_vctr</code> which has a number of methods. These are
designed to make your life as easy as possible. For example:</p>
<ul>
<li><p>The <code>print()</code> and <code>str()</code> methods are
defined in terms of <code>format()</code> so you get a pleasant,
consistent display as soon as youve made your <code>format()</code>
method.</p></li>
<li><p>You can immediately put your new vector class in a data frame
because <code>as.data.frame.vctrs_vctr()</code> does the right
thing.</p></li>
<li><p>Subsetting (<code>[</code>, <code>[[</code>, and <code>$</code>),
<code>length&lt;-</code>, and <code>rep()</code> methods automatically
preserve attributes because they use <code>vec_restore()</code>. A
default <code>vec_restore()</code> works for all classes where the
attributes are data-independent, and can easily be customised when the
attributes do depend on the data.</p></li>
<li><p>Default subset-assignment methods (<code>[&lt;-</code>,
<code>[[&lt;-</code>, and <code>$&lt;-</code>) follow the principle that
the new values should be coerced to match the existing vector. This
gives predictable behaviour and clear error messages.</p></li>
</ul>
<div id="percent-class" class="section level3">
<h3>Percent class</h3>
<p>In this section, Ill show you how to make a <code>percent</code>
class, i.e., a double vector that is printed as a percentage. We start
by defining a low-level <a href="https://adv-r.hadley.nz/s3.html#s3-constrcutor">constructor</a> to
check types and/or sizes and call <code>new_vctr()</code>.</p>
<p><code>percent</code> is built on a double vector of any length and
doesnt have any attributes.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb2-1"><a href="#cb2-1" tabindex="-1"></a>new_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">x =</span> <span class="fu">double</span>()) {</span>
<span id="cb2-2"><a href="#cb2-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_double</span>(x)) {</span>
<span id="cb2-3"><a href="#cb2-3" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`x` must be a double vector.&quot;</span>)</span>
<span id="cb2-4"><a href="#cb2-4" tabindex="-1"></a> }</span>
<span id="cb2-5"><a href="#cb2-5" tabindex="-1"></a> <span class="fu">new_vctr</span>(x, <span class="at">class =</span> <span class="st">&quot;vctrs_percent&quot;</span>)</span>
<span id="cb2-6"><a href="#cb2-6" tabindex="-1"></a>}</span>
<span id="cb2-7"><a href="#cb2-7" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">new_percent</span>(<span class="fu">c</span>(<span class="fu">seq</span>(<span class="dv">0</span>, <span class="dv">1</span>, <span class="at">length.out =</span> <span class="dv">4</span>), <span class="cn">NA</span>))</span>
<span id="cb2-9"><a href="#cb2-9" tabindex="-1"></a>x</span>
<span id="cb2-10"><a href="#cb2-10" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[5]&gt;</span></span>
<span id="cb2-11"><a href="#cb2-11" tabindex="-1"></a><span class="co">#&gt; [1] 0.0000000 0.3333333 0.6666667 1.0000000 NA</span></span>
<span id="cb2-12"><a href="#cb2-12" tabindex="-1"></a></span>
<span id="cb2-13"><a href="#cb2-13" tabindex="-1"></a><span class="fu">str</span>(x)</span>
<span id="cb2-14"><a href="#cb2-14" tabindex="-1"></a><span class="co">#&gt; vctrs_pr [1:5] 0.0000000, 0.3333333, 0.6666667, 1.0000000, NA</span></span></code></pre></div>
<p>Note that we prefix the name of the class with the name of the
package. This prevents conflicting definitions between packages. For
packages that implement only one class (such as <a href="https://blob.tidyverse.org/">blob</a>), its fine to use the
package name without prefix as the class name.</p>
<p>We then follow up with a user friendly <a href="https://adv-r.hadley.nz/s3.html#helpers">helper</a>. Here well
use <code>vec_cast()</code> to allow it to accept anything coercible to
a double:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb3-1"><a href="#cb3-1" tabindex="-1"></a>percent <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">x =</span> <span class="fu">double</span>()) {</span>
<span id="cb3-2"><a href="#cb3-2" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(x, <span class="fu">double</span>())</span>
<span id="cb3-3"><a href="#cb3-3" tabindex="-1"></a> <span class="fu">new_percent</span>(x)</span>
<span id="cb3-4"><a href="#cb3-4" tabindex="-1"></a>}</span></code></pre></div>
<p>Before you go on, check that user-friendly constructor returns a
zero-length vector when called with no arguments. This makes it easy to
use as a prototype.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb4-1"><a href="#cb4-1" tabindex="-1"></a><span class="fu">new_percent</span>()</span>
<span id="cb4-2"><a href="#cb4-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[0]&gt;</span></span>
<span id="cb4-3"><a href="#cb4-3" tabindex="-1"></a><span class="fu">percent</span>()</span>
<span id="cb4-4"><a href="#cb4-4" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[0]&gt;</span></span></code></pre></div>
<p>For the convenience of your users, consider implementing an
<code>is_percent()</code> function:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" tabindex="-1"></a>is_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb5-2"><a href="#cb5-2" tabindex="-1"></a> <span class="fu">inherits</span>(x, <span class="st">&quot;vctrs_percent&quot;</span>)</span>
<span id="cb5-3"><a href="#cb5-3" tabindex="-1"></a>}</span></code></pre></div>
</div>
<div id="format-method" class="section level3">
<h3><code>format()</code> method</h3>
<p>The first method for every class should almost always be a
<code>format()</code> method. This should return a character vector the
same length as <code>x</code>. The easiest way to do this is to rely on
one of Rs low-level formatting functions like
<code>formatC()</code>:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb6-1"><a href="#cb6-1" tabindex="-1"></a>format.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb6-2"><a href="#cb6-2" tabindex="-1"></a> out <span class="ot">&lt;-</span> <span class="fu">formatC</span>(<span class="fu">signif</span>(<span class="fu">vec_data</span>(x) <span class="sc">*</span> <span class="dv">100</span>, <span class="dv">3</span>))</span>
<span id="cb6-3"><a href="#cb6-3" tabindex="-1"></a> out[<span class="fu">is.na</span>(x)] <span class="ot">&lt;-</span> <span class="cn">NA</span></span>
<span id="cb6-4"><a href="#cb6-4" tabindex="-1"></a> out[<span class="sc">!</span><span class="fu">is.na</span>(x)] <span class="ot">&lt;-</span> <span class="fu">paste0</span>(out[<span class="sc">!</span><span class="fu">is.na</span>(x)], <span class="st">&quot;%&quot;</span>)</span>
<span id="cb6-5"><a href="#cb6-5" tabindex="-1"></a> out</span>
<span id="cb6-6"><a href="#cb6-6" tabindex="-1"></a>}</span></code></pre></div>
<div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" tabindex="-1"></a>x</span>
<span id="cb7-2"><a href="#cb7-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[5]&gt;</span></span>
<span id="cb7-3"><a href="#cb7-3" tabindex="-1"></a><span class="co">#&gt; [1] 0% 33.3% 66.7% 100% &lt;NA&gt;</span></span></code></pre></div>
<p>(Note the use of <code>vec_data()</code> so <code>format()</code>
doesnt get stuck in an infinite loop, and that I take a little care to
not convert <code>NA</code> to <code>&quot;NA&quot;</code>; this leads to better
printing.)</p>
<p>The format method is also used by data frames, tibbles, and
<code>str()</code>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb8-1"><a href="#cb8-1" tabindex="-1"></a><span class="fu">data.frame</span>(x)</span>
<span id="cb8-2"><a href="#cb8-2" tabindex="-1"></a><span class="co">#&gt; x</span></span>
<span id="cb8-3"><a href="#cb8-3" tabindex="-1"></a><span class="co">#&gt; 1 0%</span></span>
<span id="cb8-4"><a href="#cb8-4" tabindex="-1"></a><span class="co">#&gt; 2 33.3%</span></span>
<span id="cb8-5"><a href="#cb8-5" tabindex="-1"></a><span class="co">#&gt; 3 66.7%</span></span>
<span id="cb8-6"><a href="#cb8-6" tabindex="-1"></a><span class="co">#&gt; 4 100%</span></span>
<span id="cb8-7"><a href="#cb8-7" tabindex="-1"></a><span class="co">#&gt; 5 &lt;NA&gt;</span></span></code></pre></div>
<p>For optimal display, I recommend also defining an abbreviated type
name, which should be 4-5 letters for commonly used vectors. This is
used in tibbles and in <code>str()</code>:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" tabindex="-1"></a>vec_ptype_abbr.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb9-2"><a href="#cb9-2" tabindex="-1"></a> <span class="st">&quot;prcnt&quot;</span></span>
<span id="cb9-3"><a href="#cb9-3" tabindex="-1"></a>}</span>
<span id="cb9-4"><a href="#cb9-4" tabindex="-1"></a></span>
<span id="cb9-5"><a href="#cb9-5" tabindex="-1"></a>tibble<span class="sc">::</span><span class="fu">tibble</span>(x)</span>
<span id="cb9-6"><a href="#cb9-6" tabindex="-1"></a><span class="co">#&gt; # A tibble: 5 × 1</span></span>
<span id="cb9-7"><a href="#cb9-7" tabindex="-1"></a><span class="co">#&gt; x</span></span>
<span id="cb9-8"><a href="#cb9-8" tabindex="-1"></a><span class="co">#&gt; &lt;prcnt&gt;</span></span>
<span id="cb9-9"><a href="#cb9-9" tabindex="-1"></a><span class="co">#&gt; 1 0%</span></span>
<span id="cb9-10"><a href="#cb9-10" tabindex="-1"></a><span class="co">#&gt; 2 33.3%</span></span>
<span id="cb9-11"><a href="#cb9-11" tabindex="-1"></a><span class="co">#&gt; 3 66.7%</span></span>
<span id="cb9-12"><a href="#cb9-12" tabindex="-1"></a><span class="co">#&gt; 4 100%</span></span>
<span id="cb9-13"><a href="#cb9-13" tabindex="-1"></a><span class="co">#&gt; 5 NA</span></span>
<span id="cb9-14"><a href="#cb9-14" tabindex="-1"></a></span>
<span id="cb9-15"><a href="#cb9-15" tabindex="-1"></a><span class="fu">str</span>(x)</span>
<span id="cb9-16"><a href="#cb9-16" tabindex="-1"></a><span class="co">#&gt; prcnt [1:5] 0%, 33.3%, 66.7%, 100%, &lt;NA&gt;</span></span></code></pre></div>
<p>If you need more control over printing in tibbles, implement a method
for <code>pillar::pillar_shaft()</code>. See
<code>vignette(&quot;pillar&quot;, package = &quot;vctrs&quot;)</code> for details.</p>
</div>
</div>
<div id="casting-and-coercion" class="section level2">
<h2>Casting and coercion</h2>
<p>The next set of methods you are likely to need are those related to
coercion and casting. Coercion and casting are two sides of the same
coin: changing the prototype of an existing object. When the change
happens <em>implicitly</em> (e.g in <code>c()</code>) we call it
<strong>coercion</strong>; when the change happens <em>explicitly</em>
(e.g. with <code>as.integer(x)</code>), we call it
<strong>casting</strong>.</p>
<p>One of the main goals of vctrs is to put coercion and casting on a
robust theoretical footing so its possible to make accurate predictions
about what (e.g.) <code>c(x, y)</code> should do when <code>x</code> and
<code>y</code> have different prototypes. vctrs achieves this goal
through two generics:</p>
<ul>
<li><p><code>vec_ptype2(x, y)</code> defines possible set of coercions.
It returns a prototype if <code>x</code> and <code>y</code> can be
safely coerced to the same prototype; otherwise it returns an error. The
set of automatic coercions is usually quite small because too many tend
to make code harder to reason about and silently propagate
mistakes.</p></li>
<li><p><code>vec_cast(x, to)</code> defines the possible sets of casts.
It returns <code>x</code> translated to have prototype <code>to</code>,
or throws an error if the conversion isnt possible. The set of possible
casts is a superset of possible coercions because theyre requested
explicitly.</p></li>
</ul>
<div id="double-dispatch" class="section level3">
<h3>Double dispatch</h3>
<p>Both generics use <a href="https://en.wikipedia.org/wiki/Double_dispatch"><strong>double
dispatch</strong></a> which means that the implementation is selected
based on the class of two arguments, not just one. S3 does not natively
support double dispatch, so we implement our own dispatch mechanism. In
practice, this means:</p>
<ul>
<li><p>You end up with method names with two classes, like
<code>vec_ptype2.foo.bar()</code>.</p></li>
<li><p>You dont need to implement default methods (they would never be
called if you do).</p></li>
<li><p>You cant call <code>NextMethod()</code>.</p></li>
</ul>
</div>
<div id="percent" class="section level3">
<h3>Percent class</h3>
<p>Well make our percent class coercible back and forth with double
vectors.</p>
<p><code>vec_ptype2()</code> provides a user friendly error message if
the coercion doesnt exist and makes sure <code>NA</code> is handled in
a standard way. <code>NA</code> is technically a logical vector, but we
want to stand in for a missing value of any type.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" tabindex="-1"></a><span class="fu">vec_ptype2</span>(<span class="st">&quot;bogus&quot;</span>, <span class="fu">percent</span>())</span>
<span id="cb10-2"><a href="#cb10-2" tabindex="-1"></a><span class="co">#&gt; Error:</span></span>
<span id="cb10-3"><a href="#cb10-3" tabindex="-1"></a><span class="co">#&gt; ! Can&#39;t combine `&quot;bogus&quot;` &lt;character&gt; and `percent()` &lt;vctrs_percent&gt;.</span></span>
<span id="cb10-4"><a href="#cb10-4" tabindex="-1"></a><span class="fu">vec_ptype2</span>(<span class="fu">percent</span>(), <span class="cn">NA</span>)</span>
<span id="cb10-5"><a href="#cb10-5" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[0]&gt;</span></span>
<span id="cb10-6"><a href="#cb10-6" tabindex="-1"></a><span class="fu">vec_ptype2</span>(<span class="cn">NA</span>, <span class="fu">percent</span>())</span>
<span id="cb10-7"><a href="#cb10-7" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[0]&gt;</span></span></code></pre></div>
<p>By default and in simple cases, an object of the same class is
compatible with itself:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb11-1"><a href="#cb11-1" tabindex="-1"></a><span class="fu">vec_ptype2</span>(<span class="fu">percent</span>(), <span class="fu">percent</span>())</span>
<span id="cb11-2"><a href="#cb11-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[0]&gt;</span></span></code></pre></div>
<p>However this only works if the attributes for both objects are the
same. Also the default methods are a bit slower. It is always a good
idea to provide an explicit coercion method for the case of identical
classes. So well start by saying that a <code>vctrs_percent</code>
combined with a <code>vctrs_percent</code> yields a
<code>vctrs_percent</code>, which we indicate by returning a prototype
generated by the constructor.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb12-1"><a href="#cb12-1" tabindex="-1"></a>vec_ptype2.vctrs_percent.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">new_percent</span>()</span></code></pre></div>
<p>Next we define methods that say that combining a <code>percent</code>
and double should yield a <code>double</code>. We avoid returning a
<code>percent</code> here because errors in the scale (1 vs. 0.01) are
more obvious with raw numbers.</p>
<p>Because double dispatch is a bit of a hack, we need to provide two
methods. Its your responsibility to ensure that each member of the pair
returns the same result: if they dont you will get weird and
unpredictable behaviour.</p>
<p>The double dispatch mechanism requires us to refer to the underlying
type, <code>double</code>, in the method name. If we implemented
<code>vec_ptype2.vctrs_percent.numeric()</code>, it would never be
called.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb13-1"><a href="#cb13-1" tabindex="-1"></a>vec_ptype2.vctrs_percent.double <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">double</span>()</span>
<span id="cb13-2"><a href="#cb13-2" tabindex="-1"></a>vec_ptype2.double.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">double</span>()</span></code></pre></div>
<p>We can check that weve implemented this correctly with
<code>vec_ptype_show()</code>:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb14-1"><a href="#cb14-1" tabindex="-1"></a><span class="fu">vec_ptype_show</span>(<span class="fu">percent</span>(), <span class="fu">double</span>(), <span class="fu">percent</span>())</span>
<span id="cb14-2"><a href="#cb14-2" tabindex="-1"></a><span class="co">#&gt; Prototype: &lt;double&gt;</span></span>
<span id="cb14-3"><a href="#cb14-3" tabindex="-1"></a><span class="co">#&gt; 0. ( , &lt;vctrs_percent&gt; ) = &lt;vctrs_percent&gt;</span></span>
<span id="cb14-4"><a href="#cb14-4" tabindex="-1"></a><span class="co">#&gt; 1. ( &lt;vctrs_percent&gt; , &lt;double&gt; ) = &lt;double&gt; </span></span>
<span id="cb14-5"><a href="#cb14-5" tabindex="-1"></a><span class="co">#&gt; 2. ( &lt;double&gt; , &lt;vctrs_percent&gt; ) = &lt;double&gt;</span></span></code></pre></div>
<p>The <code>vec_ptype2()</code> methods define which input is the
richer type that vctrs should coerce to. However, they dont perform any
conversion. This is the job of <code>vec_cast()</code>, which we
implement next. Well provide a method to cast a percent to a
percent:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb15-1"><a href="#cb15-1" tabindex="-1"></a>vec_cast.vctrs_percent.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) x</span></code></pre></div>
<p>And then for converting back and forth between doubles. To convert a
double to a percent we use the <code>percent()</code> helper (not the
constructor; this is unvalidated user input). To convert a
<code>percent</code> to a double, we strip the attributes.</p>
<p>Note that for historical reasons the order of argument in the
signature is the opposite as for <code>vec_ptype2()</code>. The class
for <code>to</code> comes first, and the class for <code>x</code> comes
second.</p>
<p>Again, the double dispatch mechanism requires us to refer to the
underlying type, <code>double</code>, in the method name. Implementing
<code>vec_cast.vctrs_percent.numeric()</code> has no effect.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb16-1"><a href="#cb16-1" tabindex="-1"></a>vec_cast.vctrs_percent.double <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">percent</span>(x)</span>
<span id="cb16-2"><a href="#cb16-2" tabindex="-1"></a>vec_cast.double.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">vec_data</span>(x)</span></code></pre></div>
<p>Then we can check this works with <code>vec_cast()</code>:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb17-1"><a href="#cb17-1" tabindex="-1"></a><span class="fu">vec_cast</span>(<span class="fl">0.5</span>, <span class="fu">percent</span>())</span>
<span id="cb17-2"><a href="#cb17-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[1]&gt;</span></span>
<span id="cb17-3"><a href="#cb17-3" tabindex="-1"></a><span class="co">#&gt; [1] 50%</span></span>
<span id="cb17-4"><a href="#cb17-4" tabindex="-1"></a><span class="fu">vec_cast</span>(<span class="fu">percent</span>(<span class="fl">0.5</span>), <span class="fu">double</span>())</span>
<span id="cb17-5"><a href="#cb17-5" tabindex="-1"></a><span class="co">#&gt; [1] 0.5</span></span></code></pre></div>
<p>Once youve implemented <code>vec_ptype2()</code> and
<code>vec_cast()</code>, you get <code>vec_c()</code>,
<code>[&lt;-</code>, and <code>[[&lt;-</code> implementations for
free.</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb18-1"><a href="#cb18-1" tabindex="-1"></a><span class="fu">vec_c</span>(<span class="fu">percent</span>(<span class="fl">0.5</span>), <span class="dv">1</span>)</span>
<span id="cb18-2"><a href="#cb18-2" tabindex="-1"></a><span class="co">#&gt; [1] 0.5 1.0</span></span>
<span id="cb18-3"><a href="#cb18-3" tabindex="-1"></a><span class="fu">vec_c</span>(<span class="cn">NA</span>, <span class="fu">percent</span>(<span class="fl">0.5</span>))</span>
<span id="cb18-4"><a href="#cb18-4" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[2]&gt;</span></span>
<span id="cb18-5"><a href="#cb18-5" tabindex="-1"></a><span class="co">#&gt; [1] &lt;NA&gt; 50%</span></span>
<span id="cb18-6"><a href="#cb18-6" tabindex="-1"></a><span class="co"># but</span></span>
<span id="cb18-7"><a href="#cb18-7" tabindex="-1"></a><span class="fu">vec_c</span>(<span class="cn">TRUE</span>, <span class="fu">percent</span>(<span class="fl">0.5</span>))</span>
<span id="cb18-8"><a href="#cb18-8" tabindex="-1"></a><span class="co">#&gt; Error in `vec_c()`:</span></span>
<span id="cb18-9"><a href="#cb18-9" tabindex="-1"></a><span class="co">#&gt; ! Can&#39;t combine `..1` &lt;logical&gt; and `..2` &lt;vctrs_percent&gt;.</span></span>
<span id="cb18-10"><a href="#cb18-10" tabindex="-1"></a></span>
<span id="cb18-11"><a href="#cb18-11" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">percent</span>(<span class="fu">c</span>(<span class="fl">0.5</span>, <span class="dv">1</span>, <span class="dv">2</span>))</span>
<span id="cb18-12"><a href="#cb18-12" tabindex="-1"></a>x[<span class="dv">1</span><span class="sc">:</span><span class="dv">2</span>] <span class="ot">&lt;-</span> <span class="dv">2</span><span class="sc">:</span><span class="dv">1</span></span>
<span id="cb18-13"><a href="#cb18-13" tabindex="-1"></a><span class="co">#&gt; Error in `vec_restore_dispatch()`:</span></span>
<span id="cb18-14"><a href="#cb18-14" tabindex="-1"></a><span class="co">#&gt; ! Can&#39;t convert &lt;integer&gt; to &lt;vctrs_percent&gt;.</span></span>
<span id="cb18-15"><a href="#cb18-15" tabindex="-1"></a>x[[<span class="dv">3</span>]] <span class="ot">&lt;-</span> <span class="fl">0.5</span></span>
<span id="cb18-16"><a href="#cb18-16" tabindex="-1"></a>x</span>
<span id="cb18-17"><a href="#cb18-17" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_percent[3]&gt;</span></span>
<span id="cb18-18"><a href="#cb18-18" tabindex="-1"></a><span class="co">#&gt; [1] 50% 100% 50%</span></span></code></pre></div>
<p>Youll also get mostly correct behaviour for <code>c()</code>. The
exception is when you use <code>c()</code> with a base R class:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb19-1"><a href="#cb19-1" tabindex="-1"></a><span class="co"># Correct</span></span>
<span id="cb19-2"><a href="#cb19-2" tabindex="-1"></a><span class="fu">c</span>(<span class="fu">percent</span>(<span class="fl">0.5</span>), <span class="dv">1</span>)</span>
<span id="cb19-3"><a href="#cb19-3" tabindex="-1"></a><span class="co">#&gt; [1] 0.5 1.0</span></span>
<span id="cb19-4"><a href="#cb19-4" tabindex="-1"></a><span class="fu">c</span>(<span class="fu">percent</span>(<span class="fl">0.5</span>), <span class="fu">factor</span>(<span class="dv">1</span>))</span>
<span id="cb19-5"><a href="#cb19-5" tabindex="-1"></a><span class="co">#&gt; Error in `vec_c()`:</span></span>
<span id="cb19-6"><a href="#cb19-6" tabindex="-1"></a><span class="co">#&gt; ! Can&#39;t combine `..1` &lt;vctrs_percent&gt; and `..2` &lt;factor&lt;25c7e&gt;&gt;.</span></span>
<span id="cb19-7"><a href="#cb19-7" tabindex="-1"></a></span>
<span id="cb19-8"><a href="#cb19-8" tabindex="-1"></a><span class="co"># Incorrect</span></span>
<span id="cb19-9"><a href="#cb19-9" tabindex="-1"></a><span class="fu">c</span>(<span class="fu">factor</span>(<span class="dv">1</span>), <span class="fu">percent</span>(<span class="fl">0.5</span>))</span>
<span id="cb19-10"><a href="#cb19-10" tabindex="-1"></a><span class="co">#&gt; [1] 1.0 0.5</span></span></code></pre></div>
<p>Unfortunately theres no way to fix this problem with the current
design of <code>c()</code>.</p>
<p>Again, as a convenience, consider providing an
<code>as_percent()</code> function that makes use of the casts defined
in your <code>vec_cast.vctrs_percent()</code> methods:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb20-1"><a href="#cb20-1" tabindex="-1"></a>as_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb20-2"><a href="#cb20-2" tabindex="-1"></a> <span class="fu">vec_cast</span>(x, <span class="fu">new_percent</span>())</span>
<span id="cb20-3"><a href="#cb20-3" tabindex="-1"></a>}</span></code></pre></div>
<p>Occasionally, it is useful to provide conversions that go beyond
whats allowed in casting. For example, we could offer a parsing method
for character vectors. In this case, <code>as_percent()</code> should be
generic, the default method should cast, and then additional methods
should implement more flexible conversion:</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb21-1"><a href="#cb21-1" tabindex="-1"></a>as_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb21-2"><a href="#cb21-2" tabindex="-1"></a> <span class="fu">UseMethod</span>(<span class="st">&quot;as_percent&quot;</span>)</span>
<span id="cb21-3"><a href="#cb21-3" tabindex="-1"></a>}</span>
<span id="cb21-4"><a href="#cb21-4" tabindex="-1"></a></span>
<span id="cb21-5"><a href="#cb21-5" tabindex="-1"></a>as_percent.default <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb21-6"><a href="#cb21-6" tabindex="-1"></a> <span class="fu">vec_cast</span>(x, <span class="fu">new_percent</span>())</span>
<span id="cb21-7"><a href="#cb21-7" tabindex="-1"></a>}</span>
<span id="cb21-8"><a href="#cb21-8" tabindex="-1"></a></span>
<span id="cb21-9"><a href="#cb21-9" tabindex="-1"></a>as_percent.character <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb21-10"><a href="#cb21-10" tabindex="-1"></a> value <span class="ot">&lt;-</span> <span class="fu">as.numeric</span>(<span class="fu">gsub</span>(<span class="st">&quot; *% *$&quot;</span>, <span class="st">&quot;&quot;</span>, x)) <span class="sc">/</span> <span class="dv">100</span></span>
<span id="cb21-11"><a href="#cb21-11" tabindex="-1"></a> <span class="fu">new_percent</span>(value)</span>
<span id="cb21-12"><a href="#cb21-12" tabindex="-1"></a>}</span></code></pre></div>
</div>
<div id="decimal-class" class="section level3">
<h3>Decimal class</h3>
<p>Now that youve seen the basics with a very simple S3 class, well
gradually explore more complicated scenarios. This section creates a
<code>decimal</code> class that prints with the specified number of
decimal places. This is very similar to <code>percent</code> but now the
class needs an attribute: the number of decimal places to display (an
integer vector of length 1).</p>
<p>We start off as before, defining a low-level constructor, a
user-friendly constructor, a <code>format()</code> method, and a
<code>vec_ptype_abbr()</code>. Note that additional object attributes
are simply passed along to <code>new_vctr()</code>:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb22-1"><a href="#cb22-1" tabindex="-1"></a>new_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">x =</span> <span class="fu">double</span>(), <span class="at">digits =</span> <span class="dv">2</span><span class="dt">L</span>) {</span>
<span id="cb22-2"><a href="#cb22-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_double</span>(x)) {</span>
<span id="cb22-3"><a href="#cb22-3" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`x` must be a double vector.&quot;</span>)</span>
<span id="cb22-4"><a href="#cb22-4" tabindex="-1"></a> }</span>
<span id="cb22-5"><a href="#cb22-5" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_integer</span>(digits)) {</span>
<span id="cb22-6"><a href="#cb22-6" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`digits` must be an integer vector.&quot;</span>)</span>
<span id="cb22-7"><a href="#cb22-7" tabindex="-1"></a> }</span>
<span id="cb22-8"><a href="#cb22-8" tabindex="-1"></a> <span class="fu">vec_check_size</span>(digits, <span class="at">size =</span> <span class="dv">1</span><span class="dt">L</span>)</span>
<span id="cb22-9"><a href="#cb22-9" tabindex="-1"></a></span>
<span id="cb22-10"><a href="#cb22-10" tabindex="-1"></a> <span class="fu">new_vctr</span>(x, <span class="at">digits =</span> digits, <span class="at">class =</span> <span class="st">&quot;vctrs_decimal&quot;</span>)</span>
<span id="cb22-11"><a href="#cb22-11" tabindex="-1"></a>}</span>
<span id="cb22-12"><a href="#cb22-12" tabindex="-1"></a></span>
<span id="cb22-13"><a href="#cb22-13" tabindex="-1"></a>decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">x =</span> <span class="fu">double</span>(), <span class="at">digits =</span> <span class="dv">2</span><span class="dt">L</span>) {</span>
<span id="cb22-14"><a href="#cb22-14" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(x, <span class="fu">double</span>())</span>
<span id="cb22-15"><a href="#cb22-15" tabindex="-1"></a> digits <span class="ot">&lt;-</span> <span class="fu">vec_recycle</span>(<span class="fu">vec_cast</span>(digits, <span class="fu">integer</span>()), <span class="dv">1</span><span class="dt">L</span>)</span>
<span id="cb22-16"><a href="#cb22-16" tabindex="-1"></a></span>
<span id="cb22-17"><a href="#cb22-17" tabindex="-1"></a> <span class="fu">new_decimal</span>(x, <span class="at">digits =</span> digits)</span>
<span id="cb22-18"><a href="#cb22-18" tabindex="-1"></a>}</span>
<span id="cb22-19"><a href="#cb22-19" tabindex="-1"></a></span>
<span id="cb22-20"><a href="#cb22-20" tabindex="-1"></a>digits <span class="ot">&lt;-</span> <span class="cf">function</span>(x) <span class="fu">attr</span>(x, <span class="st">&quot;digits&quot;</span>)</span>
<span id="cb22-21"><a href="#cb22-21" tabindex="-1"></a></span>
<span id="cb22-22"><a href="#cb22-22" tabindex="-1"></a>format.vctrs_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb22-23"><a href="#cb22-23" tabindex="-1"></a> <span class="fu">sprintf</span>(<span class="fu">paste0</span>(<span class="st">&quot;%-0.&quot;</span>, <span class="fu">digits</span>(x), <span class="st">&quot;f&quot;</span>), x)</span>
<span id="cb22-24"><a href="#cb22-24" tabindex="-1"></a>}</span>
<span id="cb22-25"><a href="#cb22-25" tabindex="-1"></a></span>
<span id="cb22-26"><a href="#cb22-26" tabindex="-1"></a>vec_ptype_abbr.vctrs_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb22-27"><a href="#cb22-27" tabindex="-1"></a> <span class="st">&quot;dec&quot;</span></span>
<span id="cb22-28"><a href="#cb22-28" tabindex="-1"></a>}</span>
<span id="cb22-29"><a href="#cb22-29" tabindex="-1"></a></span>
<span id="cb22-30"><a href="#cb22-30" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">decimal</span>(<span class="fu">runif</span>(<span class="dv">10</span>), <span class="dv">1</span><span class="dt">L</span>)</span>
<span id="cb22-31"><a href="#cb22-31" tabindex="-1"></a>x</span>
<span id="cb22-32"><a href="#cb22-32" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_decimal[10]&gt;</span></span>
<span id="cb22-33"><a href="#cb22-33" tabindex="-1"></a><span class="co">#&gt; [1] 0.1 0.8 0.6 0.2 0.0 0.5 0.5 0.3 0.7 0.8</span></span></code></pre></div>
<p>Note that I provide a little helper to extract the
<code>digits</code> attribute. This makes the code a little easier to
read and should not be exported.</p>
<p>By default, vctrs assumes that attributes are independent of the data
and so are automatically preserved. Youll see what to do if the
attributes are data dependent in the next section.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb23-1"><a href="#cb23-1" tabindex="-1"></a>x[<span class="dv">1</span><span class="sc">:</span><span class="dv">2</span>]</span>
<span id="cb23-2"><a href="#cb23-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_decimal[2]&gt;</span></span>
<span id="cb23-3"><a href="#cb23-3" tabindex="-1"></a><span class="co">#&gt; [1] 0.1 0.8</span></span>
<span id="cb23-4"><a href="#cb23-4" tabindex="-1"></a>x[[<span class="dv">1</span>]]</span>
<span id="cb23-5"><a href="#cb23-5" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_decimal[1]&gt;</span></span>
<span id="cb23-6"><a href="#cb23-6" tabindex="-1"></a><span class="co">#&gt; [1] 0.1</span></span></code></pre></div>
<p>For the sake of exposition, well assume that <code>digits</code> is
an important attribute of the class and should be included in the full
type:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb24-1"><a href="#cb24-1" tabindex="-1"></a>vec_ptype_full.vctrs_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb24-2"><a href="#cb24-2" tabindex="-1"></a> <span class="fu">paste0</span>(<span class="st">&quot;decimal&lt;&quot;</span>, <span class="fu">digits</span>(x), <span class="st">&quot;&gt;&quot;</span>)</span>
<span id="cb24-3"><a href="#cb24-3" tabindex="-1"></a>}</span>
<span id="cb24-4"><a href="#cb24-4" tabindex="-1"></a></span>
<span id="cb24-5"><a href="#cb24-5" tabindex="-1"></a>x</span>
<span id="cb24-6"><a href="#cb24-6" tabindex="-1"></a><span class="co">#&gt; &lt;decimal&lt;1&gt;[10]&gt;</span></span>
<span id="cb24-7"><a href="#cb24-7" tabindex="-1"></a><span class="co">#&gt; [1] 0.1 0.8 0.6 0.2 0.0 0.5 0.5 0.3 0.7 0.8</span></span></code></pre></div>
<p>Now consider <code>vec_cast()</code> and <code>vec_ptype2()</code>.
Casting and coercing from one decimal to another requires a little
thought as the values of the <code>digits</code> attribute might be
different, and we need some way to reconcile them. Here Ive decided to
chose the maximum of the two; other reasonable options are to take the
value from the left-hand side or throw an error.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb25-1"><a href="#cb25-1" tabindex="-1"></a>vec_ptype2.vctrs_decimal.vctrs_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) {</span>
<span id="cb25-2"><a href="#cb25-2" tabindex="-1"></a> <span class="fu">new_decimal</span>(<span class="at">digits =</span> <span class="fu">max</span>(<span class="fu">digits</span>(x), <span class="fu">digits</span>(y)))</span>
<span id="cb25-3"><a href="#cb25-3" tabindex="-1"></a>}</span>
<span id="cb25-4"><a href="#cb25-4" tabindex="-1"></a>vec_cast.vctrs_decimal.vctrs_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) {</span>
<span id="cb25-5"><a href="#cb25-5" tabindex="-1"></a> <span class="fu">new_decimal</span>(<span class="fu">vec_data</span>(x), <span class="at">digits =</span> <span class="fu">digits</span>(to))</span>
<span id="cb25-6"><a href="#cb25-6" tabindex="-1"></a>}</span>
<span id="cb25-7"><a href="#cb25-7" tabindex="-1"></a></span>
<span id="cb25-8"><a href="#cb25-8" tabindex="-1"></a><span class="fu">vec_c</span>(<span class="fu">decimal</span>(<span class="dv">1</span><span class="sc">/</span><span class="dv">100</span>, <span class="at">digits =</span> <span class="dv">3</span>), <span class="fu">decimal</span>(<span class="dv">2</span><span class="sc">/</span><span class="dv">100</span>, <span class="at">digits =</span> <span class="dv">2</span>))</span>
<span id="cb25-9"><a href="#cb25-9" tabindex="-1"></a><span class="co">#&gt; &lt;decimal&lt;3&gt;[2]&gt;</span></span>
<span id="cb25-10"><a href="#cb25-10" tabindex="-1"></a><span class="co">#&gt; [1] 0.010 0.020</span></span></code></pre></div>
<p>Finally, I can implement coercion to and from other types, like
doubles. When automatically coercing, I choose the richer type (i.e.,
the decimal).</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb26-1"><a href="#cb26-1" tabindex="-1"></a>vec_ptype2.vctrs_decimal.double <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) x</span>
<span id="cb26-2"><a href="#cb26-2" tabindex="-1"></a>vec_ptype2.double.vctrs_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) y</span>
<span id="cb26-3"><a href="#cb26-3" tabindex="-1"></a></span>
<span id="cb26-4"><a href="#cb26-4" tabindex="-1"></a>vec_cast.vctrs_decimal.double <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">new_decimal</span>(x, <span class="at">digits =</span> <span class="fu">digits</span>(to))</span>
<span id="cb26-5"><a href="#cb26-5" tabindex="-1"></a>vec_cast.double.vctrs_decimal <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">vec_data</span>(x)</span>
<span id="cb26-6"><a href="#cb26-6" tabindex="-1"></a></span>
<span id="cb26-7"><a href="#cb26-7" tabindex="-1"></a><span class="fu">vec_c</span>(<span class="fu">decimal</span>(<span class="dv">1</span>, <span class="at">digits =</span> <span class="dv">1</span>), pi)</span>
<span id="cb26-8"><a href="#cb26-8" tabindex="-1"></a><span class="co">#&gt; &lt;decimal&lt;1&gt;[2]&gt;</span></span>
<span id="cb26-9"><a href="#cb26-9" tabindex="-1"></a><span class="co">#&gt; [1] 1.0 3.1</span></span>
<span id="cb26-10"><a href="#cb26-10" tabindex="-1"></a><span class="fu">vec_c</span>(pi, <span class="fu">decimal</span>(<span class="dv">1</span>, <span class="at">digits =</span> <span class="dv">1</span>))</span>
<span id="cb26-11"><a href="#cb26-11" tabindex="-1"></a><span class="co">#&gt; &lt;decimal&lt;1&gt;[2]&gt;</span></span>
<span id="cb26-12"><a href="#cb26-12" tabindex="-1"></a><span class="co">#&gt; [1] 3.1 1.0</span></span></code></pre></div>
<p>If type <code>x</code> has greater resolution than <code>y</code>,
there will be some inputs that lose precision. These should generate
errors using <code>stop_lossy_cast()</code>. You can see that in action
when casting from doubles to integers; only some doubles can become
integers without losing resolution.</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb27-1"><a href="#cb27-1" tabindex="-1"></a><span class="fu">vec_cast</span>(<span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">10</span>), <span class="at">to =</span> <span class="fu">integer</span>())</span>
<span id="cb27-2"><a href="#cb27-2" tabindex="-1"></a><span class="co">#&gt; [1] 1 2 10</span></span>
<span id="cb27-3"><a href="#cb27-3" tabindex="-1"></a></span>
<span id="cb27-4"><a href="#cb27-4" tabindex="-1"></a><span class="fu">vec_cast</span>(<span class="fu">c</span>(<span class="fl">1.5</span>, <span class="dv">2</span>, <span class="fl">10.5</span>), <span class="at">to =</span> <span class="fu">integer</span>())</span>
<span id="cb27-5"><a href="#cb27-5" tabindex="-1"></a><span class="co">#&gt; Error:</span></span>
<span id="cb27-6"><a href="#cb27-6" tabindex="-1"></a><span class="co">#&gt; ! Can&#39;t convert from `c(1.5, 2, 10.5)` &lt;double&gt; to &lt;integer&gt; due to loss of precision.</span></span>
<span id="cb27-7"><a href="#cb27-7" tabindex="-1"></a><span class="co">#&gt; • Locations: 1, 3</span></span></code></pre></div>
</div>
<div id="cached-sum" class="section level3">
<h3>Cached sum class</h3>
<p>The next level up in complexity is an object that has data-dependent
attributes. To explore this idea well create a vector that caches the
sum of its values. As usual, we start with low-level and user-friendly
constructors:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb28-1"><a href="#cb28-1" tabindex="-1"></a>new_cached_sum <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">x =</span> <span class="fu">double</span>(), <span class="at">sum =</span> <span class="dv">0</span><span class="dt">L</span>) {</span>
<span id="cb28-2"><a href="#cb28-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_double</span>(x)) {</span>
<span id="cb28-3"><a href="#cb28-3" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`x` must be a double vector.&quot;</span>)</span>
<span id="cb28-4"><a href="#cb28-4" tabindex="-1"></a> }</span>
<span id="cb28-5"><a href="#cb28-5" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_double</span>(sum)) {</span>
<span id="cb28-6"><a href="#cb28-6" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`sum` must be a double vector.&quot;</span>)</span>
<span id="cb28-7"><a href="#cb28-7" tabindex="-1"></a> }</span>
<span id="cb28-8"><a href="#cb28-8" tabindex="-1"></a> <span class="fu">vec_check_size</span>(sum, <span class="at">size =</span> <span class="dv">1</span><span class="dt">L</span>)</span>
<span id="cb28-9"><a href="#cb28-9" tabindex="-1"></a></span>
<span id="cb28-10"><a href="#cb28-10" tabindex="-1"></a> <span class="fu">new_vctr</span>(x, <span class="at">sum =</span> sum, <span class="at">class =</span> <span class="st">&quot;vctrs_cached_sum&quot;</span>)</span>
<span id="cb28-11"><a href="#cb28-11" tabindex="-1"></a>}</span>
<span id="cb28-12"><a href="#cb28-12" tabindex="-1"></a></span>
<span id="cb28-13"><a href="#cb28-13" tabindex="-1"></a>cached_sum <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb28-14"><a href="#cb28-14" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(x, <span class="fu">double</span>())</span>
<span id="cb28-15"><a href="#cb28-15" tabindex="-1"></a> <span class="fu">new_cached_sum</span>(x, <span class="fu">sum</span>(x))</span>
<span id="cb28-16"><a href="#cb28-16" tabindex="-1"></a>}</span></code></pre></div>
<p>For this class, we can use the default <code>format()</code> method,
and instead, well customise the <code>obj_print_footer()</code> method.
This is a good place to display user facing attributes.</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb29-1"><a href="#cb29-1" tabindex="-1"></a>obj_print_footer.vctrs_cached_sum <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb29-2"><a href="#cb29-2" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;# Sum: &quot;</span>, <span class="fu">format</span>(<span class="fu">attr</span>(x, <span class="st">&quot;sum&quot;</span>), <span class="at">digits =</span> <span class="dv">3</span>), <span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span>, <span class="at">sep =</span> <span class="st">&quot;&quot;</span>)</span>
<span id="cb29-3"><a href="#cb29-3" tabindex="-1"></a>}</span>
<span id="cb29-4"><a href="#cb29-4" tabindex="-1"></a></span>
<span id="cb29-5"><a href="#cb29-5" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">cached_sum</span>(<span class="fu">runif</span>(<span class="dv">10</span>))</span>
<span id="cb29-6"><a href="#cb29-6" tabindex="-1"></a>x</span>
<span id="cb29-7"><a href="#cb29-7" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_cached_sum[10]&gt;</span></span>
<span id="cb29-8"><a href="#cb29-8" tabindex="-1"></a><span class="co">#&gt; [1] 0.87460066 0.17494063 0.03424133 0.32038573 0.40232824 0.19566983</span></span>
<span id="cb29-9"><a href="#cb29-9" tabindex="-1"></a><span class="co">#&gt; [7] 0.40353812 0.06366146 0.38870131 0.97554784</span></span>
<span id="cb29-10"><a href="#cb29-10" tabindex="-1"></a><span class="co">#&gt; # Sum: 3.83</span></span></code></pre></div>
<p>Well also override <code>sum()</code> and <code>mean()</code> to use
the attribute. This is easiest to do with <code>vec_math()</code>, which
youll learn about later.</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb30-1"><a href="#cb30-1" tabindex="-1"></a>vec_math.vctrs_cached_sum <span class="ot">&lt;-</span> <span class="cf">function</span>(.fn, .x, ...) {</span>
<span id="cb30-2"><a href="#cb30-2" tabindex="-1"></a> <span class="fu">cat</span>(<span class="st">&quot;Using cache</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb30-3"><a href="#cb30-3" tabindex="-1"></a> <span class="cf">switch</span>(.fn,</span>
<span id="cb30-4"><a href="#cb30-4" tabindex="-1"></a> <span class="at">sum =</span> <span class="fu">attr</span>(.x, <span class="st">&quot;sum&quot;</span>),</span>
<span id="cb30-5"><a href="#cb30-5" tabindex="-1"></a> <span class="at">mean =</span> <span class="fu">attr</span>(.x, <span class="st">&quot;sum&quot;</span>) <span class="sc">/</span> <span class="fu">length</span>(.x),</span>
<span id="cb30-6"><a href="#cb30-6" tabindex="-1"></a> <span class="fu">vec_math_base</span>(.fn, .x, ...)</span>
<span id="cb30-7"><a href="#cb30-7" tabindex="-1"></a> )</span>
<span id="cb30-8"><a href="#cb30-8" tabindex="-1"></a>}</span>
<span id="cb30-9"><a href="#cb30-9" tabindex="-1"></a></span>
<span id="cb30-10"><a href="#cb30-10" tabindex="-1"></a><span class="fu">sum</span>(x)</span>
<span id="cb30-11"><a href="#cb30-11" tabindex="-1"></a><span class="co">#&gt; Using cache</span></span>
<span id="cb30-12"><a href="#cb30-12" tabindex="-1"></a><span class="co">#&gt; [1] 3.833615</span></span></code></pre></div>
<p>As mentioned above, vctrs assumes that attributes are independent of
the data. This means that when we take advantage of the default methods,
theyll work, but return the incorrect result:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb31-1"><a href="#cb31-1" tabindex="-1"></a>x[<span class="dv">1</span><span class="sc">:</span><span class="dv">2</span>]</span>
<span id="cb31-2"><a href="#cb31-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_cached_sum[2]&gt;</span></span>
<span id="cb31-3"><a href="#cb31-3" tabindex="-1"></a><span class="co">#&gt; [1] 0.8746007 0.1749406</span></span>
<span id="cb31-4"><a href="#cb31-4" tabindex="-1"></a><span class="co">#&gt; # Sum: 3.83</span></span></code></pre></div>
<p>To fix this, you need to provide a <code>vec_restore()</code> method.
Note that this method dispatches on the <code>to</code> argument.</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb32-1"><a href="#cb32-1" tabindex="-1"></a>vec_restore.vctrs_cached_sum <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ..., <span class="at">i =</span> <span class="cn">NULL</span>) {</span>
<span id="cb32-2"><a href="#cb32-2" tabindex="-1"></a> <span class="fu">new_cached_sum</span>(x, <span class="fu">sum</span>(x))</span>
<span id="cb32-3"><a href="#cb32-3" tabindex="-1"></a>}</span>
<span id="cb32-4"><a href="#cb32-4" tabindex="-1"></a></span>
<span id="cb32-5"><a href="#cb32-5" tabindex="-1"></a>x[<span class="dv">1</span>]</span>
<span id="cb32-6"><a href="#cb32-6" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_cached_sum[1]&gt;</span></span>
<span id="cb32-7"><a href="#cb32-7" tabindex="-1"></a><span class="co">#&gt; [1] 0.8746007</span></span>
<span id="cb32-8"><a href="#cb32-8" tabindex="-1"></a><span class="co">#&gt; # Sum: 0.875</span></span></code></pre></div>
<p>This works because most of the vctrs methods dispatch to the
underlying base function by first stripping off extra attributes with
<code>vec_data()</code> and then reapplying them again with
<code>vec_restore()</code>. The default <code>vec_restore()</code>
method copies over all attributes, which is not appropriate when the
attributes depend on the data.</p>
<p>Note that <code>vec_restore.class</code> is subtly different from
<code>vec_cast.class.class()</code>. <code>vec_restore()</code> is used
when restoring attributes that have been lost; <code>vec_cast()</code>
is used for coercions. This is easier to understand with a concrete
example. Imagine factors were implemented with <code>new_vctr()</code>.
<code>vec_restore.factor()</code> would restore attributes back to an
integer vector, but you would not want to allow manually casting an
integer to a factor with <code>vec_cast()</code>.</p>
</div>
</div>
<div id="record-style-objects" class="section level2">
<h2>Record-style objects</h2>
<p>Record-style objects use a list of equal-length vectors to represent
individual components of the object. The best example of this is
<code>POSIXlt</code>, which underneath the hood is a list of 11 fields
like year, month, and day. Record-style classes override
<code>length()</code> and subsetting methods to conceal this
implementation detail.</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb33-1"><a href="#cb33-1" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">as.POSIXlt</span>(<span class="fu">ISOdatetime</span>(<span class="dv">2020</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span><span class="sc">:</span><span class="dv">3</span>))</span>
<span id="cb33-2"><a href="#cb33-2" tabindex="-1"></a>x</span>
<span id="cb33-3"><a href="#cb33-3" tabindex="-1"></a><span class="co">#&gt; [1] &quot;2020-01-01 00:00:01 EST&quot; &quot;2020-01-01 00:00:02 EST&quot;</span></span>
<span id="cb33-4"><a href="#cb33-4" tabindex="-1"></a><span class="co">#&gt; [3] &quot;2020-01-01 00:00:03 EST&quot;</span></span>
<span id="cb33-5"><a href="#cb33-5" tabindex="-1"></a></span>
<span id="cb33-6"><a href="#cb33-6" tabindex="-1"></a><span class="fu">length</span>(x)</span>
<span id="cb33-7"><a href="#cb33-7" tabindex="-1"></a><span class="co">#&gt; [1] 3</span></span>
<span id="cb33-8"><a href="#cb33-8" tabindex="-1"></a><span class="fu">length</span>(<span class="fu">unclass</span>(x))</span>
<span id="cb33-9"><a href="#cb33-9" tabindex="-1"></a><span class="co">#&gt; [1] 11</span></span>
<span id="cb33-10"><a href="#cb33-10" tabindex="-1"></a></span>
<span id="cb33-11"><a href="#cb33-11" tabindex="-1"></a>x[[<span class="dv">1</span>]] <span class="co"># the first date time</span></span>
<span id="cb33-12"><a href="#cb33-12" tabindex="-1"></a><span class="co">#&gt; [1] &quot;2020-01-01 00:00:01 EST&quot;</span></span>
<span id="cb33-13"><a href="#cb33-13" tabindex="-1"></a><span class="fu">unclass</span>(x)[[<span class="dv">1</span>]] <span class="co"># the first component, the number of seconds</span></span>
<span id="cb33-14"><a href="#cb33-14" tabindex="-1"></a><span class="co">#&gt; [1] 1 2 3</span></span></code></pre></div>
<p>vctrs makes it easy to create new record-style classes using
<code>new_rcrd()</code>, which has a wide selection of default
methods.</p>
<div id="rational-class" class="section level3">
<h3>Rational class</h3>
<p>A fraction, or rational number, can be represented by a pair of
integer vectors representing the numerator (the number on top) and the
denominator (the number on bottom), where the length of each vector must
be the same. To represent such a data structure we turn to a new base
data type: the record (or rcrd for short).</p>
<p>As usual we start with low-level and user-friendly constructors. The
low-level constructor calls <code>new_rcrd()</code>, which needs a named
list of equal-length vectors.</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb34-1"><a href="#cb34-1" tabindex="-1"></a>new_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">n =</span> <span class="fu">integer</span>(), <span class="at">d =</span> <span class="fu">integer</span>()) {</span>
<span id="cb34-2"><a href="#cb34-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_integer</span>(n)) {</span>
<span id="cb34-3"><a href="#cb34-3" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`n` must be an integer vector.&quot;</span>)</span>
<span id="cb34-4"><a href="#cb34-4" tabindex="-1"></a> }</span>
<span id="cb34-5"><a href="#cb34-5" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_integer</span>(d)) {</span>
<span id="cb34-6"><a href="#cb34-6" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`d` must be an integer vector.&quot;</span>)</span>
<span id="cb34-7"><a href="#cb34-7" tabindex="-1"></a> }</span>
<span id="cb34-8"><a href="#cb34-8" tabindex="-1"></a></span>
<span id="cb34-9"><a href="#cb34-9" tabindex="-1"></a> <span class="fu">new_rcrd</span>(<span class="fu">list</span>(<span class="at">n =</span> n, <span class="at">d =</span> d), <span class="at">class =</span> <span class="st">&quot;vctrs_rational&quot;</span>)</span>
<span id="cb34-10"><a href="#cb34-10" tabindex="-1"></a>}</span></code></pre></div>
<p>Our user friendly constructor casts <code>n</code> and <code>d</code>
to integers and recycles them to the same length.</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb35-1"><a href="#cb35-1" tabindex="-1"></a>rational <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">n =</span> <span class="fu">integer</span>(), <span class="at">d =</span> <span class="fu">integer</span>()) {</span>
<span id="cb35-2"><a href="#cb35-2" tabindex="-1"></a> <span class="fu">c</span>(n, d) <span class="sc">%&lt;-%</span> <span class="fu">vec_cast_common</span>(n, d, <span class="at">.to =</span> <span class="fu">integer</span>())</span>
<span id="cb35-3"><a href="#cb35-3" tabindex="-1"></a> <span class="fu">c</span>(n, d) <span class="sc">%&lt;-%</span> <span class="fu">vec_recycle_common</span>(n, d)</span>
<span id="cb35-4"><a href="#cb35-4" tabindex="-1"></a></span>
<span id="cb35-5"><a href="#cb35-5" tabindex="-1"></a> <span class="fu">new_rational</span>(n, d)</span>
<span id="cb35-6"><a href="#cb35-6" tabindex="-1"></a>}</span>
<span id="cb35-7"><a href="#cb35-7" tabindex="-1"></a></span>
<span id="cb35-8"><a href="#cb35-8" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">rational</span>(<span class="dv">1</span>, <span class="dv">1</span><span class="sc">:</span><span class="dv">10</span>)</span></code></pre></div>
<p>Behind the scenes, <code>x</code> is a named list with two elements.
But those details are hidden so that it behaves like a vector:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb36-1"><a href="#cb36-1" tabindex="-1"></a><span class="fu">names</span>(x)</span>
<span id="cb36-2"><a href="#cb36-2" tabindex="-1"></a><span class="co">#&gt; NULL</span></span>
<span id="cb36-3"><a href="#cb36-3" tabindex="-1"></a><span class="fu">length</span>(x)</span>
<span id="cb36-4"><a href="#cb36-4" tabindex="-1"></a><span class="co">#&gt; [1] 10</span></span></code></pre></div>
<p>To access the underlying fields we need to use <code>field()</code>
and <code>fields()</code>:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb37-1"><a href="#cb37-1" tabindex="-1"></a><span class="fu">fields</span>(x)</span>
<span id="cb37-2"><a href="#cb37-2" tabindex="-1"></a><span class="co">#&gt; [1] &quot;n&quot; &quot;d&quot;</span></span>
<span id="cb37-3"><a href="#cb37-3" tabindex="-1"></a><span class="fu">field</span>(x, <span class="st">&quot;n&quot;</span>)</span>
<span id="cb37-4"><a href="#cb37-4" tabindex="-1"></a><span class="co">#&gt; [1] 1 1 1 1 1 1 1 1 1 1</span></span></code></pre></div>
<p>Notice that we cant <code>print()</code> or <code>str()</code> the
new rational vector <code>x</code> yet. Printing causes an error:</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb38-1"><a href="#cb38-1" tabindex="-1"></a>x</span>
<span id="cb38-2"><a href="#cb38-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_rational[10]&gt;</span></span>
<span id="cb38-3"><a href="#cb38-3" tabindex="-1"></a><span class="co">#&gt; Error in `format()`:</span></span>
<span id="cb38-4"><a href="#cb38-4" tabindex="-1"></a><span class="co">#&gt; ! `format.vctrs_rational()` not implemented.</span></span>
<span id="cb38-5"><a href="#cb38-5" tabindex="-1"></a></span>
<span id="cb38-6"><a href="#cb38-6" tabindex="-1"></a><span class="fu">str</span>(x)</span>
<span id="cb38-7"><a href="#cb38-7" tabindex="-1"></a><span class="co">#&gt; Error in `format()`:</span></span>
<span id="cb38-8"><a href="#cb38-8" tabindex="-1"></a><span class="co">#&gt; ! `format.vctrs_rational()` not implemented.</span></span></code></pre></div>
<p>This is because we havent defined how our class can be printed from
the underlying data. Note that if you want to look under the hood during
development, you can always call <code>vec_data(x)</code>.</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb39-1"><a href="#cb39-1" tabindex="-1"></a><span class="fu">vec_data</span>(x)</span>
<span id="cb39-2"><a href="#cb39-2" tabindex="-1"></a><span class="co">#&gt; n d</span></span>
<span id="cb39-3"><a href="#cb39-3" tabindex="-1"></a><span class="co">#&gt; 1 1 1</span></span>
<span id="cb39-4"><a href="#cb39-4" tabindex="-1"></a><span class="co">#&gt; 2 1 2</span></span>
<span id="cb39-5"><a href="#cb39-5" tabindex="-1"></a><span class="co">#&gt; 3 1 3</span></span>
<span id="cb39-6"><a href="#cb39-6" tabindex="-1"></a><span class="co">#&gt; 4 1 4</span></span>
<span id="cb39-7"><a href="#cb39-7" tabindex="-1"></a><span class="co">#&gt; 5 1 5</span></span>
<span id="cb39-8"><a href="#cb39-8" tabindex="-1"></a><span class="co">#&gt; 6 1 6</span></span>
<span id="cb39-9"><a href="#cb39-9" tabindex="-1"></a><span class="co">#&gt; 7 1 7</span></span>
<span id="cb39-10"><a href="#cb39-10" tabindex="-1"></a><span class="co">#&gt; 8 1 8</span></span>
<span id="cb39-11"><a href="#cb39-11" tabindex="-1"></a><span class="co">#&gt; 9 1 9</span></span>
<span id="cb39-12"><a href="#cb39-12" tabindex="-1"></a><span class="co">#&gt; 10 1 10</span></span>
<span id="cb39-13"><a href="#cb39-13" tabindex="-1"></a></span>
<span id="cb39-14"><a href="#cb39-14" tabindex="-1"></a><span class="fu">str</span>(<span class="fu">vec_data</span>(x))</span>
<span id="cb39-15"><a href="#cb39-15" tabindex="-1"></a><span class="co">#&gt; &#39;data.frame&#39;: 10 obs. of 2 variables:</span></span>
<span id="cb39-16"><a href="#cb39-16" tabindex="-1"></a><span class="co">#&gt; $ n: int 1 1 1 1 1 1 1 1 1 1</span></span>
<span id="cb39-17"><a href="#cb39-17" tabindex="-1"></a><span class="co">#&gt; $ d: int 1 2 3 4 5 6 7 8 9 10</span></span></code></pre></div>
<p>It is generally best to define a formatting method early in the
development of a class. The format method defines how to display the
class so that it can be printed in the normal way:</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb40-1"><a href="#cb40-1" tabindex="-1"></a>format.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb40-2"><a href="#cb40-2" tabindex="-1"></a> n <span class="ot">&lt;-</span> <span class="fu">field</span>(x, <span class="st">&quot;n&quot;</span>)</span>
<span id="cb40-3"><a href="#cb40-3" tabindex="-1"></a> d <span class="ot">&lt;-</span> <span class="fu">field</span>(x, <span class="st">&quot;d&quot;</span>)</span>
<span id="cb40-4"><a href="#cb40-4" tabindex="-1"></a></span>
<span id="cb40-5"><a href="#cb40-5" tabindex="-1"></a> out <span class="ot">&lt;-</span> <span class="fu">paste0</span>(n, <span class="st">&quot;/&quot;</span>, d)</span>
<span id="cb40-6"><a href="#cb40-6" tabindex="-1"></a> out[<span class="fu">is.na</span>(n) <span class="sc">|</span> <span class="fu">is.na</span>(d)] <span class="ot">&lt;-</span> <span class="cn">NA</span></span>
<span id="cb40-7"><a href="#cb40-7" tabindex="-1"></a></span>
<span id="cb40-8"><a href="#cb40-8" tabindex="-1"></a> out</span>
<span id="cb40-9"><a href="#cb40-9" tabindex="-1"></a>}</span>
<span id="cb40-10"><a href="#cb40-10" tabindex="-1"></a></span>
<span id="cb40-11"><a href="#cb40-11" tabindex="-1"></a>vec_ptype_abbr.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) <span class="st">&quot;rtnl&quot;</span></span>
<span id="cb40-12"><a href="#cb40-12" tabindex="-1"></a>vec_ptype_full.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) <span class="st">&quot;rational&quot;</span></span>
<span id="cb40-13"><a href="#cb40-13" tabindex="-1"></a></span>
<span id="cb40-14"><a href="#cb40-14" tabindex="-1"></a>x</span>
<span id="cb40-15"><a href="#cb40-15" tabindex="-1"></a><span class="co">#&gt; &lt;rational[10]&gt;</span></span>
<span id="cb40-16"><a href="#cb40-16" tabindex="-1"></a><span class="co">#&gt; [1] 1/1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 1/9 1/10</span></span></code></pre></div>
<p>vctrs uses the <code>format()</code> method in <code>str()</code>,
hiding the underlying implementation details from the user:</p>
<div class="sourceCode" id="cb41"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb41-1"><a href="#cb41-1" tabindex="-1"></a><span class="fu">str</span>(x)</span>
<span id="cb41-2"><a href="#cb41-2" tabindex="-1"></a><span class="co">#&gt; rtnl [1:10] 1/1, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10</span></span></code></pre></div>
<p>For <code>rational</code>, <code>vec_ptype2()</code> and
<code>vec_cast()</code> follow the same pattern as
<code>percent()</code>. We allow coercion from integer and to
doubles.</p>
<div class="sourceCode" id="cb42"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb42-1"><a href="#cb42-1" tabindex="-1"></a>vec_ptype2.vctrs_rational.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">new_rational</span>()</span>
<span id="cb42-2"><a href="#cb42-2" tabindex="-1"></a>vec_ptype2.vctrs_rational.integer <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">new_rational</span>()</span>
<span id="cb42-3"><a href="#cb42-3" tabindex="-1"></a>vec_ptype2.integer.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">new_rational</span>()</span>
<span id="cb42-4"><a href="#cb42-4" tabindex="-1"></a></span>
<span id="cb42-5"><a href="#cb42-5" tabindex="-1"></a>vec_cast.vctrs_rational.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) x</span>
<span id="cb42-6"><a href="#cb42-6" tabindex="-1"></a>vec_cast.double.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">field</span>(x, <span class="st">&quot;n&quot;</span>) <span class="sc">/</span> <span class="fu">field</span>(x, <span class="st">&quot;d&quot;</span>)</span>
<span id="cb42-7"><a href="#cb42-7" tabindex="-1"></a>vec_cast.vctrs_rational.integer <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">rational</span>(x, <span class="dv">1</span>)</span>
<span id="cb42-8"><a href="#cb42-8" tabindex="-1"></a></span>
<span id="cb42-9"><a href="#cb42-9" tabindex="-1"></a><span class="fu">vec_c</span>(<span class="fu">rational</span>(<span class="dv">1</span>, <span class="dv">2</span>), <span class="dv">1</span><span class="dt">L</span>, <span class="cn">NA</span>)</span>
<span id="cb42-10"><a href="#cb42-10" tabindex="-1"></a><span class="co">#&gt; &lt;rational[3]&gt;</span></span>
<span id="cb42-11"><a href="#cb42-11" tabindex="-1"></a><span class="co">#&gt; [1] 1/2 1/1 &lt;NA&gt;</span></span></code></pre></div>
</div>
<div id="decimal2-class" class="section level3">
<h3>Decimal2 class</h3>
<p>The previous implementation of <code>decimal</code> was built on top
of doubles. This is a bad idea because decimal vectors are typically
used when you care about precise values (i.e., dollars and cents in a
bank account), and double values suffer from floating point
problems.</p>
<p>A better implementation of a decimal class would be to use pair of
integers, one for the value to the left of the decimal point, and the
other for the value to the right (divided by a <code>scale</code>). The
following code is a very quick sketch of how you might start creating
such a class:</p>
<div class="sourceCode" id="cb43"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb43-1"><a href="#cb43-1" tabindex="-1"></a>new_decimal2 <span class="ot">&lt;-</span> <span class="cf">function</span>(l, r, <span class="at">scale =</span> <span class="dv">2</span><span class="dt">L</span>) {</span>
<span id="cb43-2"><a href="#cb43-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_integer</span>(l)) {</span>
<span id="cb43-3"><a href="#cb43-3" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`l` must be an integer vector.&quot;</span>)</span>
<span id="cb43-4"><a href="#cb43-4" tabindex="-1"></a> }</span>
<span id="cb43-5"><a href="#cb43-5" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_integer</span>(r)) {</span>
<span id="cb43-6"><a href="#cb43-6" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`r` must be an integer vector.&quot;</span>)</span>
<span id="cb43-7"><a href="#cb43-7" tabindex="-1"></a> }</span>
<span id="cb43-8"><a href="#cb43-8" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_integer</span>(scale)) {</span>
<span id="cb43-9"><a href="#cb43-9" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`scale` must be an integer vector.&quot;</span>)</span>
<span id="cb43-10"><a href="#cb43-10" tabindex="-1"></a> }</span>
<span id="cb43-11"><a href="#cb43-11" tabindex="-1"></a> <span class="fu">vec_check_size</span>(scale, <span class="at">size =</span> <span class="dv">1</span><span class="dt">L</span>)</span>
<span id="cb43-12"><a href="#cb43-12" tabindex="-1"></a></span>
<span id="cb43-13"><a href="#cb43-13" tabindex="-1"></a> <span class="fu">new_rcrd</span>(<span class="fu">list</span>(<span class="at">l =</span> l, <span class="at">r =</span> r), <span class="at">scale =</span> scale, <span class="at">class =</span> <span class="st">&quot;vctrs_decimal2&quot;</span>)</span>
<span id="cb43-14"><a href="#cb43-14" tabindex="-1"></a>}</span>
<span id="cb43-15"><a href="#cb43-15" tabindex="-1"></a></span>
<span id="cb43-16"><a href="#cb43-16" tabindex="-1"></a>decimal2 <span class="ot">&lt;-</span> <span class="cf">function</span>(l, r, <span class="at">scale =</span> <span class="dv">2</span><span class="dt">L</span>) {</span>
<span id="cb43-17"><a href="#cb43-17" tabindex="-1"></a> l <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(l, <span class="fu">integer</span>())</span>
<span id="cb43-18"><a href="#cb43-18" tabindex="-1"></a> r <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(r, <span class="fu">integer</span>())</span>
<span id="cb43-19"><a href="#cb43-19" tabindex="-1"></a> <span class="fu">c</span>(l, r) <span class="sc">%&lt;-%</span> <span class="fu">vec_recycle_common</span>(l, r)</span>
<span id="cb43-20"><a href="#cb43-20" tabindex="-1"></a> scale <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(scale, <span class="fu">integer</span>())</span>
<span id="cb43-21"><a href="#cb43-21" tabindex="-1"></a></span>
<span id="cb43-22"><a href="#cb43-22" tabindex="-1"></a> <span class="co"># should check that r &lt; 10^scale</span></span>
<span id="cb43-23"><a href="#cb43-23" tabindex="-1"></a> <span class="fu">new_decimal2</span>(<span class="at">l =</span> l, <span class="at">r =</span> r, <span class="at">scale =</span> scale)</span>
<span id="cb43-24"><a href="#cb43-24" tabindex="-1"></a>}</span>
<span id="cb43-25"><a href="#cb43-25" tabindex="-1"></a></span>
<span id="cb43-26"><a href="#cb43-26" tabindex="-1"></a>format.vctrs_decimal2 <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb43-27"><a href="#cb43-27" tabindex="-1"></a> val <span class="ot">&lt;-</span> <span class="fu">field</span>(x, <span class="st">&quot;l&quot;</span>) <span class="sc">+</span> <span class="fu">field</span>(x, <span class="st">&quot;r&quot;</span>) <span class="sc">/</span> <span class="dv">10</span><span class="sc">^</span><span class="fu">attr</span>(x, <span class="st">&quot;scale&quot;</span>)</span>
<span id="cb43-28"><a href="#cb43-28" tabindex="-1"></a> <span class="fu">sprintf</span>(<span class="fu">paste0</span>(<span class="st">&quot;%.0&quot;</span>, <span class="fu">attr</span>(x, <span class="st">&quot;scale&quot;</span>), <span class="st">&quot;f&quot;</span>), val)</span>
<span id="cb43-29"><a href="#cb43-29" tabindex="-1"></a>}</span>
<span id="cb43-30"><a href="#cb43-30" tabindex="-1"></a></span>
<span id="cb43-31"><a href="#cb43-31" tabindex="-1"></a><span class="fu">decimal2</span>(<span class="dv">10</span>, <span class="fu">c</span>(<span class="dv">0</span>, <span class="dv">5</span>, <span class="dv">99</span>))</span>
<span id="cb43-32"><a href="#cb43-32" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_decimal2[3]&gt;</span></span>
<span id="cb43-33"><a href="#cb43-33" tabindex="-1"></a><span class="co">#&gt; [1] 10.00 10.05 10.99</span></span></code></pre></div>
</div>
</div>
<div id="equality-and-comparison" class="section level2">
<h2>Equality and comparison</h2>
<p>vctrs provides four “proxy” generics. Two of these let you control
how your class determines equality and comparison:</p>
<ul>
<li><p><code>vec_proxy_equal()</code> returns a data vector suitable for
comparison. It underpins <code>==</code>, <code>!=</code>,
<code>unique()</code>, <code>anyDuplicated()</code>, and
<code>is.na()</code>.</p></li>
<li><p><code>vec_proxy_compare()</code> specifies how to compare the
elements of your vector. This proxy is used in <code>&lt;</code>,
<code>&lt;=</code>, <code>&gt;=</code>, <code>&gt;</code>,
<code>min()</code>, <code>max()</code>, <code>median()</code>, and
<code>quantile()</code>.</p></li>
</ul>
<p>Two other proxy generic are used for sorting for unordered data types
and for accessing the raw data for exotic storage formats:</p>
<ul>
<li><p><code>vec_proxy_order()</code> specifies how to sort the elements
of your vector. It is used in <code>xtfrm()</code>, which in turn is
called by the <code>order()</code> and <code>sort()</code>
functions.</p>
<p>This proxy was added to implement the behaviour of lists, which are
sortable (their order proxy sorts by first occurrence) but not
comparable (comparison operators cause an error). Its default
implementation for other classes calls <code>vec_proxy_compare()</code>
and you normally dont need to implement this proxy.</p></li>
<li><p><code>vec_proxy()</code> returns the actual data of a vector.
This is useful when you store the data in a field of your class. Most of
the time, you shouldnt need to implement
<code>vec_proxy()</code>.</p></li>
</ul>
<p>The default behavior is as follows:</p>
<ul>
<li><code>vec_proxy_equal()</code> calls <code>vec_proxy()</code></li>
<li><code>vec_proxy_compare()</code> calls
<code>vec_proxy_equal()</code></li>
<li><code>vec_proxy_order()</code> calls
<code>vec_proxy_compare()</code></li>
</ul>
<p>You should only implement these proxies when some preprocessing on
the data is needed to make elements comparable. In that case, defining
these methods will get you a lot of behaviour for relatively little
work.</p>
<p>These proxy functions should always return a simple object (either a
bare vector or a data frame) that possesses the same properties as your
class. This permits efficient implementation of the vctrs internals
because it allows dispatch to happen once in R, and then efficient
computations can be written in C.</p>
<div id="rational-class-1" class="section level3">
<h3>Rational class</h3>
<p>Lets explore these ideas by with the rational class we started on
above. By default, <code>vec_proxy()</code> converts a record to a data
frame, and the default comparison works column by column:</p>
<div class="sourceCode" id="cb44"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb44-1"><a href="#cb44-1" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">rational</span>(<span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">1</span>, <span class="dv">2</span>), <span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">2</span>))</span>
<span id="cb44-2"><a href="#cb44-2" tabindex="-1"></a>x</span>
<span id="cb44-3"><a href="#cb44-3" tabindex="-1"></a><span class="co">#&gt; &lt;rational[4]&gt;</span></span>
<span id="cb44-4"><a href="#cb44-4" tabindex="-1"></a><span class="co">#&gt; [1] 1/1 2/1 1/2 2/2</span></span>
<span id="cb44-5"><a href="#cb44-5" tabindex="-1"></a></span>
<span id="cb44-6"><a href="#cb44-6" tabindex="-1"></a><span class="fu">vec_proxy</span>(x)</span>
<span id="cb44-7"><a href="#cb44-7" tabindex="-1"></a><span class="co">#&gt; n d</span></span>
<span id="cb44-8"><a href="#cb44-8" tabindex="-1"></a><span class="co">#&gt; 1 1 1</span></span>
<span id="cb44-9"><a href="#cb44-9" tabindex="-1"></a><span class="co">#&gt; 2 2 1</span></span>
<span id="cb44-10"><a href="#cb44-10" tabindex="-1"></a><span class="co">#&gt; 3 1 2</span></span>
<span id="cb44-11"><a href="#cb44-11" tabindex="-1"></a><span class="co">#&gt; 4 2 2</span></span>
<span id="cb44-12"><a href="#cb44-12" tabindex="-1"></a></span>
<span id="cb44-13"><a href="#cb44-13" tabindex="-1"></a>x <span class="sc">==</span> <span class="fu">rational</span>(<span class="dv">1</span>, <span class="dv">1</span>)</span>
<span id="cb44-14"><a href="#cb44-14" tabindex="-1"></a><span class="co">#&gt; [1] TRUE FALSE FALSE FALSE</span></span></code></pre></div>
<p>This makes sense as a default but isnt correct here because
<code>rational(1, 1)</code> represents the same number as
<code>rational(2, 2)</code>, so they should be equal. We can fix that by
implementing a <code>vec_proxy_equal()</code> method that divides
<code>n</code> and <code>d</code> by their greatest common divisor:</p>
<div class="sourceCode" id="cb45"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb45-1"><a href="#cb45-1" tabindex="-1"></a><span class="co"># Thanks to Matthew Lundberg: https://stackoverflow.com/a/21504113/16632</span></span>
<span id="cb45-2"><a href="#cb45-2" tabindex="-1"></a>gcd <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y) {</span>
<span id="cb45-3"><a href="#cb45-3" tabindex="-1"></a> r <span class="ot">&lt;-</span> x <span class="sc">%%</span> y</span>
<span id="cb45-4"><a href="#cb45-4" tabindex="-1"></a> <span class="fu">ifelse</span>(r, <span class="fu">gcd</span>(y, r), y)</span>
<span id="cb45-5"><a href="#cb45-5" tabindex="-1"></a>}</span>
<span id="cb45-6"><a href="#cb45-6" tabindex="-1"></a></span>
<span id="cb45-7"><a href="#cb45-7" tabindex="-1"></a>vec_proxy_equal.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb45-8"><a href="#cb45-8" tabindex="-1"></a> n <span class="ot">&lt;-</span> <span class="fu">field</span>(x, <span class="st">&quot;n&quot;</span>)</span>
<span id="cb45-9"><a href="#cb45-9" tabindex="-1"></a> d <span class="ot">&lt;-</span> <span class="fu">field</span>(x, <span class="st">&quot;d&quot;</span>)</span>
<span id="cb45-10"><a href="#cb45-10" tabindex="-1"></a> gcd <span class="ot">&lt;-</span> <span class="fu">gcd</span>(n, d)</span>
<span id="cb45-11"><a href="#cb45-11" tabindex="-1"></a></span>
<span id="cb45-12"><a href="#cb45-12" tabindex="-1"></a> <span class="fu">data.frame</span>(<span class="at">n =</span> n <span class="sc">/</span> gcd, <span class="at">d =</span> d <span class="sc">/</span> gcd)</span>
<span id="cb45-13"><a href="#cb45-13" tabindex="-1"></a>}</span>
<span id="cb45-14"><a href="#cb45-14" tabindex="-1"></a><span class="fu">vec_proxy_equal</span>(x)</span>
<span id="cb45-15"><a href="#cb45-15" tabindex="-1"></a><span class="co">#&gt; n d</span></span>
<span id="cb45-16"><a href="#cb45-16" tabindex="-1"></a><span class="co">#&gt; 1 1 1</span></span>
<span id="cb45-17"><a href="#cb45-17" tabindex="-1"></a><span class="co">#&gt; 2 2 1</span></span>
<span id="cb45-18"><a href="#cb45-18" tabindex="-1"></a><span class="co">#&gt; 3 1 2</span></span>
<span id="cb45-19"><a href="#cb45-19" tabindex="-1"></a><span class="co">#&gt; 4 1 1</span></span>
<span id="cb45-20"><a href="#cb45-20" tabindex="-1"></a></span>
<span id="cb45-21"><a href="#cb45-21" tabindex="-1"></a>x <span class="sc">==</span> <span class="fu">rational</span>(<span class="dv">1</span>, <span class="dv">1</span>)</span>
<span id="cb45-22"><a href="#cb45-22" tabindex="-1"></a><span class="co">#&gt; [1] TRUE FALSE FALSE TRUE</span></span></code></pre></div>
<p><code>vec_proxy_equal()</code> is also used by
<code>unique()</code>:</p>
<div class="sourceCode" id="cb46"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb46-1"><a href="#cb46-1" tabindex="-1"></a><span class="fu">unique</span>(x)</span>
<span id="cb46-2"><a href="#cb46-2" tabindex="-1"></a><span class="co">#&gt; &lt;rational[3]&gt;</span></span>
<span id="cb46-3"><a href="#cb46-3" tabindex="-1"></a><span class="co">#&gt; [1] 1/1 2/1 1/2</span></span></code></pre></div>
<p>We now need to fix the comparison operations similarly, since
comparison currently happens lexicographically by <code>n</code>, then
by <code>d</code>:</p>
<div class="sourceCode" id="cb47"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb47-1"><a href="#cb47-1" tabindex="-1"></a><span class="fu">rational</span>(<span class="dv">1</span>, <span class="dv">2</span>) <span class="sc">&lt;</span> <span class="fu">rational</span>(<span class="dv">2</span>, <span class="dv">3</span>)</span>
<span id="cb47-2"><a href="#cb47-2" tabindex="-1"></a><span class="co">#&gt; [1] TRUE</span></span>
<span id="cb47-3"><a href="#cb47-3" tabindex="-1"></a><span class="fu">rational</span>(<span class="dv">2</span>, <span class="dv">4</span>) <span class="sc">&lt;</span> <span class="fu">rational</span>(<span class="dv">2</span>, <span class="dv">3</span>)</span>
<span id="cb47-4"><a href="#cb47-4" tabindex="-1"></a><span class="co">#&gt; [1] TRUE</span></span></code></pre></div>
<p>The easiest fix is to convert the fraction to a floating point number
and use this as a proxy:</p>
<div class="sourceCode" id="cb48"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb48-1"><a href="#cb48-1" tabindex="-1"></a>vec_proxy_compare.vctrs_rational <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb48-2"><a href="#cb48-2" tabindex="-1"></a> <span class="fu">field</span>(x, <span class="st">&quot;n&quot;</span>) <span class="sc">/</span> <span class="fu">field</span>(x, <span class="st">&quot;d&quot;</span>)</span>
<span id="cb48-3"><a href="#cb48-3" tabindex="-1"></a>}</span>
<span id="cb48-4"><a href="#cb48-4" tabindex="-1"></a></span>
<span id="cb48-5"><a href="#cb48-5" tabindex="-1"></a><span class="fu">rational</span>(<span class="dv">2</span>, <span class="dv">4</span>) <span class="sc">&lt;</span> <span class="fu">rational</span>(<span class="dv">2</span>, <span class="dv">3</span>)</span>
<span id="cb48-6"><a href="#cb48-6" tabindex="-1"></a><span class="co">#&gt; [1] TRUE</span></span></code></pre></div>
<p>This also fixes <code>sort()</code>, because the default
implementation of <code>vec_proxy_order()</code> calls
<code>vec_proxy_compare()</code>.</p>
<div class="sourceCode" id="cb49"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb49-1"><a href="#cb49-1" tabindex="-1"></a><span class="fu">sort</span>(x)</span>
<span id="cb49-2"><a href="#cb49-2" tabindex="-1"></a><span class="co">#&gt; &lt;rational[4]&gt;</span></span>
<span id="cb49-3"><a href="#cb49-3" tabindex="-1"></a><span class="co">#&gt; [1] 1/2 1/1 2/2 2/1</span></span></code></pre></div>
<p>(We could have used the same approach in
<code>vec_proxy_equal()</code>, but when working with floating point
numbers its not necessarily true that <code>x == y</code> implies that
<code>d * x == d * y</code>.)</p>
</div>
<div id="polynomial-class" class="section level3">
<h3>Polynomial class</h3>
<p>A related problem occurs if we build our vector on top of a list. The
following code defines a polynomial class that represents polynomials
(like <code>1 + 3x - 2x^2</code>) using a list of integer vectors (like
<code>c(1, 3, -2)</code>). Note the use of <code>new_list_of()</code> in
the constructor.</p>
<div class="sourceCode" id="cb50"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb50-1"><a href="#cb50-1" tabindex="-1"></a>poly <span class="ot">&lt;-</span> <span class="cf">function</span>(...) {</span>
<span id="cb50-2"><a href="#cb50-2" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">vec_cast_common</span>(..., <span class="at">.to =</span> <span class="fu">integer</span>())</span>
<span id="cb50-3"><a href="#cb50-3" tabindex="-1"></a> <span class="fu">new_poly</span>(x)</span>
<span id="cb50-4"><a href="#cb50-4" tabindex="-1"></a>}</span>
<span id="cb50-5"><a href="#cb50-5" tabindex="-1"></a>new_poly <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb50-6"><a href="#cb50-6" tabindex="-1"></a> <span class="fu">new_list_of</span>(x, <span class="at">ptype =</span> <span class="fu">integer</span>(), <span class="at">class =</span> <span class="st">&quot;vctrs_poly_list&quot;</span>)</span>
<span id="cb50-7"><a href="#cb50-7" tabindex="-1"></a>}</span>
<span id="cb50-8"><a href="#cb50-8" tabindex="-1"></a></span>
<span id="cb50-9"><a href="#cb50-9" tabindex="-1"></a>vec_ptype_full.vctrs_poly_list <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) <span class="st">&quot;polynomial&quot;</span></span>
<span id="cb50-10"><a href="#cb50-10" tabindex="-1"></a>vec_ptype_abbr.vctrs_poly_list <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) <span class="st">&quot;poly&quot;</span></span>
<span id="cb50-11"><a href="#cb50-11" tabindex="-1"></a></span>
<span id="cb50-12"><a href="#cb50-12" tabindex="-1"></a>format.vctrs_poly_list <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb50-13"><a href="#cb50-13" tabindex="-1"></a> format_one <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb50-14"><a href="#cb50-14" tabindex="-1"></a> <span class="cf">if</span> (<span class="fu">length</span>(x) <span class="sc">==</span> <span class="dv">0</span>) {</span>
<span id="cb50-15"><a href="#cb50-15" tabindex="-1"></a> <span class="fu">return</span>(<span class="st">&quot;&quot;</span>)</span>
<span id="cb50-16"><a href="#cb50-16" tabindex="-1"></a> }</span>
<span id="cb50-17"><a href="#cb50-17" tabindex="-1"></a></span>
<span id="cb50-18"><a href="#cb50-18" tabindex="-1"></a> <span class="cf">if</span> (<span class="fu">length</span>(x) <span class="sc">==</span> <span class="dv">1</span>) {</span>
<span id="cb50-19"><a href="#cb50-19" tabindex="-1"></a> <span class="fu">format</span>(x)</span>
<span id="cb50-20"><a href="#cb50-20" tabindex="-1"></a> } <span class="cf">else</span> {</span>
<span id="cb50-21"><a href="#cb50-21" tabindex="-1"></a> suffix <span class="ot">&lt;-</span> <span class="fu">c</span>(<span class="fu">paste0</span>(<span class="st">&quot;\u22C5x^&quot;</span>, <span class="fu">seq</span>(<span class="fu">length</span>(x) <span class="sc">-</span> <span class="dv">1</span>, <span class="dv">1</span>)), <span class="st">&quot;&quot;</span>)</span>
<span id="cb50-22"><a href="#cb50-22" tabindex="-1"></a> out <span class="ot">&lt;-</span> <span class="fu">paste0</span>(x, suffix)</span>
<span id="cb50-23"><a href="#cb50-23" tabindex="-1"></a> out <span class="ot">&lt;-</span> out[x <span class="sc">!=</span> <span class="dv">0</span><span class="dt">L</span>]</span>
<span id="cb50-24"><a href="#cb50-24" tabindex="-1"></a> <span class="fu">paste0</span>(out, <span class="at">collapse =</span> <span class="st">&quot; + &quot;</span>)</span>
<span id="cb50-25"><a href="#cb50-25" tabindex="-1"></a> }</span>
<span id="cb50-26"><a href="#cb50-26" tabindex="-1"></a> }</span>
<span id="cb50-27"><a href="#cb50-27" tabindex="-1"></a></span>
<span id="cb50-28"><a href="#cb50-28" tabindex="-1"></a> <span class="fu">vapply</span>(x, format_one, <span class="fu">character</span>(<span class="dv">1</span>))</span>
<span id="cb50-29"><a href="#cb50-29" tabindex="-1"></a>}</span>
<span id="cb50-30"><a href="#cb50-30" tabindex="-1"></a></span>
<span id="cb50-31"><a href="#cb50-31" tabindex="-1"></a>obj_print_data.vctrs_poly_list <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb50-32"><a href="#cb50-32" tabindex="-1"></a> <span class="cf">if</span> (<span class="fu">length</span>(x) <span class="sc">!=</span> <span class="dv">0</span>) {</span>
<span id="cb50-33"><a href="#cb50-33" tabindex="-1"></a> <span class="fu">print</span>(<span class="fu">format</span>(x), <span class="at">quote =</span> <span class="cn">FALSE</span>)</span>
<span id="cb50-34"><a href="#cb50-34" tabindex="-1"></a> }</span>
<span id="cb50-35"><a href="#cb50-35" tabindex="-1"></a>}</span>
<span id="cb50-36"><a href="#cb50-36" tabindex="-1"></a></span>
<span id="cb50-37"><a href="#cb50-37" tabindex="-1"></a>p <span class="ot">&lt;-</span> <span class="fu">poly</span>(<span class="dv">1</span>, <span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">2</span>), <span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">1</span>))</span>
<span id="cb50-38"><a href="#cb50-38" tabindex="-1"></a>p</span>
<span id="cb50-39"><a href="#cb50-39" tabindex="-1"></a><span class="co">#&gt; &lt;polynomial[3]&gt;</span></span>
<span id="cb50-40"><a href="#cb50-40" tabindex="-1"></a><span class="co">#&gt; [1] 1 1⋅x^4 + 2 1⋅x^2 + 1</span></span></code></pre></div>
<p>The resulting objects will inherit from the
<code>vctrs_list_of</code> class, which provides tailored methods for
<code>$</code>, <code>[[</code>, the corresponding assignment operators,
and other methods.</p>
<div class="sourceCode" id="cb51"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb51-1"><a href="#cb51-1" tabindex="-1"></a><span class="fu">class</span>(p)</span>
<span id="cb51-2"><a href="#cb51-2" tabindex="-1"></a><span class="co">#&gt; [1] &quot;vctrs_poly_list&quot; &quot;vctrs_list_of&quot; &quot;vctrs_vctr&quot; &quot;list&quot;</span></span>
<span id="cb51-3"><a href="#cb51-3" tabindex="-1"></a>p[<span class="dv">2</span>]</span>
<span id="cb51-4"><a href="#cb51-4" tabindex="-1"></a><span class="co">#&gt; &lt;polynomial[1]&gt;</span></span>
<span id="cb51-5"><a href="#cb51-5" tabindex="-1"></a><span class="co">#&gt; [1] 1⋅x^4 + 2</span></span>
<span id="cb51-6"><a href="#cb51-6" tabindex="-1"></a>p[[<span class="dv">2</span>]]</span>
<span id="cb51-7"><a href="#cb51-7" tabindex="-1"></a><span class="co">#&gt; [1] 1 0 0 0 2</span></span></code></pre></div>
<p>The class implements the list interface:</p>
<div class="sourceCode" id="cb52"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb52-1"><a href="#cb52-1" tabindex="-1"></a><span class="fu">obj_is_list</span>(p)</span>
<span id="cb52-2"><a href="#cb52-2" tabindex="-1"></a><span class="co">#&gt; [1] TRUE</span></span></code></pre></div>
<p>This is fine for the internal implementation of this class but it
would be more appropriate if it behaved like an atomic vector rather
than a list.</p>
<div id="make-an-atomic-polynomial-vector" class="section level4">
<h4>Make an atomic polynomial vector</h4>
<p>An atomic vector is a vector like integer or character for which
<code>[[</code> returns the same type. Unlike lists, you cant reach
inside an atomic vector.</p>
<p>To make the polynomial class an atomic vector, well wrap the
internal <code>list_of()</code> class within a record vector. Usually
records are used because they can store several fields of data for each
observation. Here we have only one, but we use the class anyway to
inherit its atomicity.</p>
<div class="sourceCode" id="cb53"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb53-1"><a href="#cb53-1" tabindex="-1"></a>poly <span class="ot">&lt;-</span> <span class="cf">function</span>(...) {</span>
<span id="cb53-2"><a href="#cb53-2" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">vec_cast_common</span>(..., <span class="at">.to =</span> <span class="fu">integer</span>())</span>
<span id="cb53-3"><a href="#cb53-3" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">new_poly</span>(x)</span>
<span id="cb53-4"><a href="#cb53-4" tabindex="-1"></a> <span class="fu">new_rcrd</span>(<span class="fu">list</span>(<span class="at">data =</span> x), <span class="at">class =</span> <span class="st">&quot;vctrs_poly&quot;</span>)</span>
<span id="cb53-5"><a href="#cb53-5" tabindex="-1"></a>}</span>
<span id="cb53-6"><a href="#cb53-6" tabindex="-1"></a>format.vctrs_poly <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb53-7"><a href="#cb53-7" tabindex="-1"></a> <span class="fu">format</span>(<span class="fu">field</span>(x, <span class="st">&quot;data&quot;</span>))</span>
<span id="cb53-8"><a href="#cb53-8" tabindex="-1"></a>}</span></code></pre></div>
<p>The new <code>format()</code> method delegates to the one we wrote
for the internal list. The vector looks just like before:</p>
<div class="sourceCode" id="cb54"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb54-1"><a href="#cb54-1" tabindex="-1"></a>p <span class="ot">&lt;-</span> <span class="fu">poly</span>(<span class="dv">1</span>, <span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">2</span>), <span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">1</span>))</span>
<span id="cb54-2"><a href="#cb54-2" tabindex="-1"></a>p</span>
<span id="cb54-3"><a href="#cb54-3" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_poly[3]&gt;</span></span>
<span id="cb54-4"><a href="#cb54-4" tabindex="-1"></a><span class="co">#&gt; [1] 1 1⋅x^4 + 2 1⋅x^2 + 1</span></span></code></pre></div>
<p>Making the class atomic means that <code>obj_is_list()</code> now
returns <code>FALSE</code>. This prevents recursive algorithms that
traverse lists from reaching too far inside the polynomial
internals.</p>
<div class="sourceCode" id="cb55"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb55-1"><a href="#cb55-1" tabindex="-1"></a><span class="fu">obj_is_list</span>(p)</span>
<span id="cb55-2"><a href="#cb55-2" tabindex="-1"></a><span class="co">#&gt; [1] FALSE</span></span></code></pre></div>
<p>Most importantly, it prevents users from reaching into the internals
with <code>[[</code>:</p>
<div class="sourceCode" id="cb56"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb56-1"><a href="#cb56-1" tabindex="-1"></a>p[[<span class="dv">2</span>]]</span>
<span id="cb56-2"><a href="#cb56-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_poly[1]&gt;</span></span>
<span id="cb56-3"><a href="#cb56-3" tabindex="-1"></a><span class="co">#&gt; [1] 1⋅x^4 + 2</span></span></code></pre></div>
</div>
<div id="implementing-equality-and-comparison" class="section level4">
<h4>Implementing equality and comparison</h4>
<p>Equality works out of the box because we can tell if two integer
vectors are equal:</p>
<div class="sourceCode" id="cb57"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb57-1"><a href="#cb57-1" tabindex="-1"></a>p <span class="sc">==</span> <span class="fu">poly</span>(<span class="fu">c</span>(<span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">1</span>))</span>
<span id="cb57-2"><a href="#cb57-2" tabindex="-1"></a><span class="co">#&gt; [1] FALSE FALSE TRUE</span></span></code></pre></div>
<p>We cant compare individual elements, because the data is stored in a
list and by default lists are not comparable:</p>
<div class="sourceCode" id="cb58"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb58-1"><a href="#cb58-1" tabindex="-1"></a>p <span class="sc">&lt;</span> p[<span class="dv">2</span>]</span>
<span id="cb58-2"><a href="#cb58-2" tabindex="-1"></a><span class="co">#&gt; Error in `vec_proxy_compare()`:</span></span>
<span id="cb58-3"><a href="#cb58-3" tabindex="-1"></a><span class="co">#&gt; ! `vec_proxy_compare.vctrs_poly_list()` not supported.</span></span></code></pre></div>
<p>To enable comparison, we implement a <code>vec_proxy_compare()</code>
method:</p>
<div class="sourceCode" id="cb59"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb59-1"><a href="#cb59-1" tabindex="-1"></a>vec_proxy_compare.vctrs_poly <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb59-2"><a href="#cb59-2" tabindex="-1"></a> <span class="co"># Get the list inside the record vector</span></span>
<span id="cb59-3"><a href="#cb59-3" tabindex="-1"></a> x_raw <span class="ot">&lt;-</span> <span class="fu">vec_data</span>(<span class="fu">field</span>(x, <span class="st">&quot;data&quot;</span>))</span>
<span id="cb59-4"><a href="#cb59-4" tabindex="-1"></a></span>
<span id="cb59-5"><a href="#cb59-5" tabindex="-1"></a> <span class="co"># First figure out the maximum length</span></span>
<span id="cb59-6"><a href="#cb59-6" tabindex="-1"></a> n <span class="ot">&lt;-</span> <span class="fu">max</span>(<span class="fu">vapply</span>(x_raw, length, <span class="fu">integer</span>(<span class="dv">1</span>)))</span>
<span id="cb59-7"><a href="#cb59-7" tabindex="-1"></a></span>
<span id="cb59-8"><a href="#cb59-8" tabindex="-1"></a> <span class="co"># Then expand all vectors to this length by filling in with zeros</span></span>
<span id="cb59-9"><a href="#cb59-9" tabindex="-1"></a> full <span class="ot">&lt;-</span> <span class="fu">lapply</span>(x_raw, <span class="cf">function</span>(x) <span class="fu">c</span>(<span class="fu">rep</span>(<span class="dv">0</span><span class="dt">L</span>, n <span class="sc">-</span> <span class="fu">length</span>(x)), x))</span>
<span id="cb59-10"><a href="#cb59-10" tabindex="-1"></a></span>
<span id="cb59-11"><a href="#cb59-11" tabindex="-1"></a> <span class="co"># Then turn into a data frame</span></span>
<span id="cb59-12"><a href="#cb59-12" tabindex="-1"></a> <span class="fu">as.data.frame</span>(<span class="fu">do.call</span>(rbind, full))</span>
<span id="cb59-13"><a href="#cb59-13" tabindex="-1"></a>}</span>
<span id="cb59-14"><a href="#cb59-14" tabindex="-1"></a></span>
<span id="cb59-15"><a href="#cb59-15" tabindex="-1"></a>p <span class="sc">&lt;</span> p[<span class="dv">2</span>]</span>
<span id="cb59-16"><a href="#cb59-16" tabindex="-1"></a><span class="co">#&gt; [1] TRUE FALSE TRUE</span></span></code></pre></div>
<p>Often, this is sufficient to also implement <code>sort()</code>.
However, for lists, there is already a default
<code>vec_proxy_order()</code> method that sorts by first
occurrence:</p>
<div class="sourceCode" id="cb60"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb60-1"><a href="#cb60-1" tabindex="-1"></a><span class="fu">sort</span>(p)</span>
<span id="cb60-2"><a href="#cb60-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_poly[3]&gt;</span></span>
<span id="cb60-3"><a href="#cb60-3" tabindex="-1"></a><span class="co">#&gt; [1] 1 1⋅x^2 + 1 1⋅x^4 + 2</span></span>
<span id="cb60-4"><a href="#cb60-4" tabindex="-1"></a><span class="fu">sort</span>(p[<span class="fu">c</span>(<span class="dv">1</span><span class="sc">:</span><span class="dv">3</span>, <span class="dv">1</span><span class="sc">:</span><span class="dv">2</span>)])</span>
<span id="cb60-5"><a href="#cb60-5" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_poly[5]&gt;</span></span>
<span id="cb60-6"><a href="#cb60-6" tabindex="-1"></a><span class="co">#&gt; [1] 1 1 1⋅x^2 + 1 1⋅x^4 + 2 1⋅x^4 + 2</span></span></code></pre></div>
<p>To ensure consistency between ordering and comparison, we forward
<code>vec_proxy_order()</code> to <code>vec_proxy_compare()</code>:</p>
<div class="sourceCode" id="cb61"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb61-1"><a href="#cb61-1" tabindex="-1"></a>vec_proxy_order.vctrs_poly <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb61-2"><a href="#cb61-2" tabindex="-1"></a> <span class="fu">vec_proxy_compare</span>(x, ...)</span>
<span id="cb61-3"><a href="#cb61-3" tabindex="-1"></a>}</span>
<span id="cb61-4"><a href="#cb61-4" tabindex="-1"></a></span>
<span id="cb61-5"><a href="#cb61-5" tabindex="-1"></a><span class="fu">sort</span>(p)</span>
<span id="cb61-6"><a href="#cb61-6" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_poly[3]&gt;</span></span>
<span id="cb61-7"><a href="#cb61-7" tabindex="-1"></a><span class="co">#&gt; [1] 1 1⋅x^2 + 1 1⋅x^4 + 2</span></span></code></pre></div>
</div>
</div>
</div>
<div id="arithmetic" class="section level2">
<h2>Arithmetic</h2>
<p>vctrs also provides two mathematical generics that allow you to
define a broad swath of mathematical behaviour at once:</p>
<ul>
<li><p><code>vec_math(fn, x, ...)</code> specifies the behaviour of
mathematical functions like <code>abs()</code>, <code>sum()</code>, and
<code>mean()</code>. (Note that <code>var()</code> and <code>sd()</code>
cant be overridden, see <code>?vec_math()</code> for the complete list
supported by <code>vec_math()</code>.)</p></li>
<li><p><code>vec_arith(op, x, y)</code> specifies the behaviour of the
arithmetic operations like <code>+</code>, <code>-</code>, and
<code>%%</code>. (See <code>?vec_arith()</code> for the complete
list.)</p></li>
</ul>
<p>Both generics define the behaviour for multiple functions because
<code>sum.vctrs_vctr(x)</code> calls
<code>vec_math.vctrs_vctr(&quot;sum&quot;, x)</code>, and <code>x + y</code> calls
<code>vec_math.x_class.y_class(&quot;+&quot;, x, y)</code>. Theyre accompanied by
<code>vec_math_base()</code> and <code>vec_arith_base()</code> which
make it easy to call the underlying base R functions.</p>
<p><code>vec_arith()</code> uses double dispatch and needs the following
standard boilerplate:</p>
<div class="sourceCode" id="cb62"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb62-1"><a href="#cb62-1" tabindex="-1"></a>vec_arith.MYCLASS <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb62-2"><a href="#cb62-2" tabindex="-1"></a> <span class="fu">UseMethod</span>(<span class="st">&quot;vec_arith.MYCLASS&quot;</span>, y)</span>
<span id="cb62-3"><a href="#cb62-3" tabindex="-1"></a>}</span>
<span id="cb62-4"><a href="#cb62-4" tabindex="-1"></a>vec_arith.MYCLASS.default <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb62-5"><a href="#cb62-5" tabindex="-1"></a> <span class="fu">stop_incompatible_op</span>(op, x, y)</span>
<span id="cb62-6"><a href="#cb62-6" tabindex="-1"></a>}</span></code></pre></div>
<p>Correctly exporting <code>vec_arith()</code> methods from a package
is currently a little awkward. See the instructions in the Arithmetic
section of the “Implementing a vctrs S3 class in a package” section
below.</p>
<div id="cached-sum-class" class="section level3">
<h3>Cached sum class</h3>
<p>I showed an example of <code>vec_math()</code> to define
<code>sum()</code> and <code>mean()</code> methods for
<code>cached_sum</code>. Now lets talk about exactly how it works. Most
<code>vec_math()</code> functions will have a similar form. You use a
switch statement to handle the methods that you care about and fall back
to <code>vec_math_base()</code> for those that you dont care about.</p>
<div class="sourceCode" id="cb63"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb63-1"><a href="#cb63-1" tabindex="-1"></a>vec_math.vctrs_cached_sum <span class="ot">&lt;-</span> <span class="cf">function</span>(.fn, .x, ...) {</span>
<span id="cb63-2"><a href="#cb63-2" tabindex="-1"></a> <span class="cf">switch</span>(.fn,</span>
<span id="cb63-3"><a href="#cb63-3" tabindex="-1"></a> <span class="at">sum =</span> <span class="fu">attr</span>(.x, <span class="st">&quot;sum&quot;</span>),</span>
<span id="cb63-4"><a href="#cb63-4" tabindex="-1"></a> <span class="at">mean =</span> <span class="fu">attr</span>(.x, <span class="st">&quot;sum&quot;</span>) <span class="sc">/</span> <span class="fu">length</span>(.x),</span>
<span id="cb63-5"><a href="#cb63-5" tabindex="-1"></a> <span class="fu">vec_math_base</span>(.fn, .x, ...)</span>
<span id="cb63-6"><a href="#cb63-6" tabindex="-1"></a> )</span>
<span id="cb63-7"><a href="#cb63-7" tabindex="-1"></a>}</span></code></pre></div>
</div>
<div id="meter-class" class="section level3">
<h3>Meter class</h3>
<p>To explore the infix arithmetic operators exposed by
<code>vec_arith()</code> Ill create a new class that represents a
measurement in <code>meter</code>s:</p>
<div class="sourceCode" id="cb64"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb64-1"><a href="#cb64-1" tabindex="-1"></a>new_meter <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb64-2"><a href="#cb64-2" tabindex="-1"></a> <span class="fu">stopifnot</span>(<span class="fu">is.double</span>(x))</span>
<span id="cb64-3"><a href="#cb64-3" tabindex="-1"></a> <span class="fu">new_vctr</span>(x, <span class="at">class =</span> <span class="st">&quot;vctrs_meter&quot;</span>)</span>
<span id="cb64-4"><a href="#cb64-4" tabindex="-1"></a>}</span>
<span id="cb64-5"><a href="#cb64-5" tabindex="-1"></a></span>
<span id="cb64-6"><a href="#cb64-6" tabindex="-1"></a>format.vctrs_meter <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb64-7"><a href="#cb64-7" tabindex="-1"></a> <span class="fu">paste0</span>(<span class="fu">format</span>(<span class="fu">vec_data</span>(x)), <span class="st">&quot; m&quot;</span>)</span>
<span id="cb64-8"><a href="#cb64-8" tabindex="-1"></a>}</span>
<span id="cb64-9"><a href="#cb64-9" tabindex="-1"></a></span>
<span id="cb64-10"><a href="#cb64-10" tabindex="-1"></a>meter <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb64-11"><a href="#cb64-11" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(x, <span class="fu">double</span>())</span>
<span id="cb64-12"><a href="#cb64-12" tabindex="-1"></a> <span class="fu">new_meter</span>(x)</span>
<span id="cb64-13"><a href="#cb64-13" tabindex="-1"></a>}</span>
<span id="cb64-14"><a href="#cb64-14" tabindex="-1"></a></span>
<span id="cb64-15"><a href="#cb64-15" tabindex="-1"></a>x <span class="ot">&lt;-</span> <span class="fu">meter</span>(<span class="dv">1</span><span class="sc">:</span><span class="dv">10</span>)</span>
<span id="cb64-16"><a href="#cb64-16" tabindex="-1"></a>x</span>
<span id="cb64-17"><a href="#cb64-17" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[10]&gt;</span></span>
<span id="cb64-18"><a href="#cb64-18" tabindex="-1"></a><span class="co">#&gt; [1] 1 m 2 m 3 m 4 m 5 m 6 m 7 m 8 m 9 m 10 m</span></span></code></pre></div>
<p>Because <code>meter</code> is built on top of a double vector, basic
mathematic operations work:</p>
<div class="sourceCode" id="cb65"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb65-1"><a href="#cb65-1" tabindex="-1"></a><span class="fu">sum</span>(x)</span>
<span id="cb65-2"><a href="#cb65-2" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb65-3"><a href="#cb65-3" tabindex="-1"></a><span class="co">#&gt; [1] 55 m</span></span>
<span id="cb65-4"><a href="#cb65-4" tabindex="-1"></a><span class="fu">mean</span>(x)</span>
<span id="cb65-5"><a href="#cb65-5" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb65-6"><a href="#cb65-6" tabindex="-1"></a><span class="co">#&gt; [1] 5.5 m</span></span></code></pre></div>
<p>But we cant do arithmetic:</p>
<div class="sourceCode" id="cb66"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb66-1"><a href="#cb66-1" tabindex="-1"></a>x <span class="sc">+</span> <span class="dv">1</span></span>
<span id="cb66-2"><a href="#cb66-2" tabindex="-1"></a><span class="co">#&gt; Error in `vec_arith()`:</span></span>
<span id="cb66-3"><a href="#cb66-3" tabindex="-1"></a><span class="co">#&gt; ! &lt;vctrs_meter&gt; + &lt;double&gt; is not permitted</span></span>
<span id="cb66-4"><a href="#cb66-4" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">10</span>) <span class="sc">+</span> <span class="fu">meter</span>(<span class="dv">1</span>)</span>
<span id="cb66-5"><a href="#cb66-5" tabindex="-1"></a><span class="co">#&gt; Error in `vec_arith()`:</span></span>
<span id="cb66-6"><a href="#cb66-6" tabindex="-1"></a><span class="co">#&gt; ! &lt;vctrs_meter&gt; + &lt;vctrs_meter&gt; is not permitted</span></span>
<span id="cb66-7"><a href="#cb66-7" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">10</span>) <span class="sc">*</span> <span class="dv">3</span></span>
<span id="cb66-8"><a href="#cb66-8" tabindex="-1"></a><span class="co">#&gt; Error in `vec_arith()`:</span></span>
<span id="cb66-9"><a href="#cb66-9" tabindex="-1"></a><span class="co">#&gt; ! &lt;vctrs_meter&gt; * &lt;double&gt; is not permitted</span></span></code></pre></div>
<p>To allow these infix functions to work, well need to provide
<code>vec_arith()</code> generic. But before we do that, lets think
about what combinations of inputs we should support:</p>
<ul>
<li><p>It makes sense to add and subtract meters: that yields another
meter. We can divide a meter by another meter (yielding a unitless
number), but we cant multiply meters (because that would yield an
area).</p></li>
<li><p>For a combination of meter and number multiplication and division
by a number are acceptable. Addition and subtraction dont make much
sense as we, strictly speaking, are dealing with objects of different
nature.</p></li>
</ul>
<p><code>vec_arith()</code> is another function that uses double
dispatch, so as usual we start with a template.</p>
<div class="sourceCode" id="cb67"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb67-1"><a href="#cb67-1" tabindex="-1"></a>vec_arith.vctrs_meter <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb67-2"><a href="#cb67-2" tabindex="-1"></a> <span class="fu">UseMethod</span>(<span class="st">&quot;vec_arith.vctrs_meter&quot;</span>, y)</span>
<span id="cb67-3"><a href="#cb67-3" tabindex="-1"></a>}</span>
<span id="cb67-4"><a href="#cb67-4" tabindex="-1"></a>vec_arith.vctrs_meter.default <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb67-5"><a href="#cb67-5" tabindex="-1"></a> <span class="fu">stop_incompatible_op</span>(op, x, y)</span>
<span id="cb67-6"><a href="#cb67-6" tabindex="-1"></a>}</span></code></pre></div>
<p>Then write the method for two meter objects. We use a switch
statement to cover the cases we care about and
<code>stop_incompatible_op()</code> to throw an informative error
message for everything else.</p>
<div class="sourceCode" id="cb68"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb68-1"><a href="#cb68-1" tabindex="-1"></a>vec_arith.vctrs_meter.vctrs_meter <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb68-2"><a href="#cb68-2" tabindex="-1"></a> <span class="cf">switch</span>(</span>
<span id="cb68-3"><a href="#cb68-3" tabindex="-1"></a> op,</span>
<span id="cb68-4"><a href="#cb68-4" tabindex="-1"></a> <span class="st">&quot;+&quot;</span> <span class="ot">=</span> ,</span>
<span id="cb68-5"><a href="#cb68-5" tabindex="-1"></a> <span class="st">&quot;-&quot;</span> <span class="ot">=</span> <span class="fu">new_meter</span>(<span class="fu">vec_arith_base</span>(op, x, y)),</span>
<span id="cb68-6"><a href="#cb68-6" tabindex="-1"></a> <span class="st">&quot;/&quot;</span> <span class="ot">=</span> <span class="fu">vec_arith_base</span>(op, x, y),</span>
<span id="cb68-7"><a href="#cb68-7" tabindex="-1"></a> <span class="fu">stop_incompatible_op</span>(op, x, y)</span>
<span id="cb68-8"><a href="#cb68-8" tabindex="-1"></a> )</span>
<span id="cb68-9"><a href="#cb68-9" tabindex="-1"></a>}</span>
<span id="cb68-10"><a href="#cb68-10" tabindex="-1"></a></span>
<span id="cb68-11"><a href="#cb68-11" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">10</span>) <span class="sc">+</span> <span class="fu">meter</span>(<span class="dv">1</span>)</span>
<span id="cb68-12"><a href="#cb68-12" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb68-13"><a href="#cb68-13" tabindex="-1"></a><span class="co">#&gt; [1] 11 m</span></span>
<span id="cb68-14"><a href="#cb68-14" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">10</span>) <span class="sc">-</span> <span class="fu">meter</span>(<span class="dv">1</span>)</span>
<span id="cb68-15"><a href="#cb68-15" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb68-16"><a href="#cb68-16" tabindex="-1"></a><span class="co">#&gt; [1] 9 m</span></span>
<span id="cb68-17"><a href="#cb68-17" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">10</span>) <span class="sc">/</span> <span class="fu">meter</span>(<span class="dv">1</span>)</span>
<span id="cb68-18"><a href="#cb68-18" tabindex="-1"></a><span class="co">#&gt; [1] 10</span></span>
<span id="cb68-19"><a href="#cb68-19" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">10</span>) <span class="sc">*</span> <span class="fu">meter</span>(<span class="dv">1</span>)</span>
<span id="cb68-20"><a href="#cb68-20" tabindex="-1"></a><span class="co">#&gt; Error in `vec_arith()`:</span></span>
<span id="cb68-21"><a href="#cb68-21" tabindex="-1"></a><span class="co">#&gt; ! &lt;vctrs_meter&gt; * &lt;vctrs_meter&gt; is not permitted</span></span></code></pre></div>
<p>Next we write the pair of methods for arithmetic with a meter and a
number. These are almost identical, but while <code>meter(10) / 2</code>
makes sense, <code>2 / meter(10)</code> does not (and neither do
addition and subtraction). To support both doubles and integers as
operands, we dispatch over <code>numeric</code> here instead of
<code>double</code>.</p>
<div class="sourceCode" id="cb69"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb69-1"><a href="#cb69-1" tabindex="-1"></a>vec_arith.vctrs_meter.numeric <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb69-2"><a href="#cb69-2" tabindex="-1"></a> <span class="cf">switch</span>(</span>
<span id="cb69-3"><a href="#cb69-3" tabindex="-1"></a> op,</span>
<span id="cb69-4"><a href="#cb69-4" tabindex="-1"></a> <span class="st">&quot;/&quot;</span> <span class="ot">=</span> ,</span>
<span id="cb69-5"><a href="#cb69-5" tabindex="-1"></a> <span class="st">&quot;*&quot;</span> <span class="ot">=</span> <span class="fu">new_meter</span>(<span class="fu">vec_arith_base</span>(op, x, y)),</span>
<span id="cb69-6"><a href="#cb69-6" tabindex="-1"></a> <span class="fu">stop_incompatible_op</span>(op, x, y)</span>
<span id="cb69-7"><a href="#cb69-7" tabindex="-1"></a> )</span>
<span id="cb69-8"><a href="#cb69-8" tabindex="-1"></a>}</span>
<span id="cb69-9"><a href="#cb69-9" tabindex="-1"></a>vec_arith.numeric.vctrs_meter <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb69-10"><a href="#cb69-10" tabindex="-1"></a> <span class="cf">switch</span>(</span>
<span id="cb69-11"><a href="#cb69-11" tabindex="-1"></a> op,</span>
<span id="cb69-12"><a href="#cb69-12" tabindex="-1"></a> <span class="st">&quot;*&quot;</span> <span class="ot">=</span> <span class="fu">new_meter</span>(<span class="fu">vec_arith_base</span>(op, x, y)),</span>
<span id="cb69-13"><a href="#cb69-13" tabindex="-1"></a> <span class="fu">stop_incompatible_op</span>(op, x, y)</span>
<span id="cb69-14"><a href="#cb69-14" tabindex="-1"></a> )</span>
<span id="cb69-15"><a href="#cb69-15" tabindex="-1"></a>}</span>
<span id="cb69-16"><a href="#cb69-16" tabindex="-1"></a></span>
<span id="cb69-17"><a href="#cb69-17" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">2</span>) <span class="sc">*</span> <span class="dv">10</span></span>
<span id="cb69-18"><a href="#cb69-18" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb69-19"><a href="#cb69-19" tabindex="-1"></a><span class="co">#&gt; [1] 20 m</span></span>
<span id="cb69-20"><a href="#cb69-20" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">2</span>) <span class="sc">*</span> <span class="fu">as.integer</span>(<span class="dv">10</span>)</span>
<span id="cb69-21"><a href="#cb69-21" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb69-22"><a href="#cb69-22" tabindex="-1"></a><span class="co">#&gt; [1] 20 m</span></span>
<span id="cb69-23"><a href="#cb69-23" tabindex="-1"></a><span class="dv">10</span> <span class="sc">*</span> <span class="fu">meter</span>(<span class="dv">2</span>)</span>
<span id="cb69-24"><a href="#cb69-24" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb69-25"><a href="#cb69-25" tabindex="-1"></a><span class="co">#&gt; [1] 20 m</span></span>
<span id="cb69-26"><a href="#cb69-26" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">20</span>) <span class="sc">/</span> <span class="dv">10</span></span>
<span id="cb69-27"><a href="#cb69-27" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb69-28"><a href="#cb69-28" tabindex="-1"></a><span class="co">#&gt; [1] 2 m</span></span>
<span id="cb69-29"><a href="#cb69-29" tabindex="-1"></a><span class="dv">10</span> <span class="sc">/</span> <span class="fu">meter</span>(<span class="dv">20</span>)</span>
<span id="cb69-30"><a href="#cb69-30" tabindex="-1"></a><span class="co">#&gt; Error in `vec_arith()`:</span></span>
<span id="cb69-31"><a href="#cb69-31" tabindex="-1"></a><span class="co">#&gt; ! &lt;double&gt; / &lt;vctrs_meter&gt; is not permitted</span></span>
<span id="cb69-32"><a href="#cb69-32" tabindex="-1"></a><span class="fu">meter</span>(<span class="dv">20</span>) <span class="sc">+</span> <span class="dv">10</span></span>
<span id="cb69-33"><a href="#cb69-33" tabindex="-1"></a><span class="co">#&gt; Error in `vec_arith()`:</span></span>
<span id="cb69-34"><a href="#cb69-34" tabindex="-1"></a><span class="co">#&gt; ! &lt;vctrs_meter&gt; + &lt;double&gt; is not permitted</span></span></code></pre></div>
<p>For completeness, we also need
<code>vec_arith.vctrs_meter.MISSING</code> for the unary <code>+</code>
and <code>-</code> operators:</p>
<div class="sourceCode" id="cb70"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb70-1"><a href="#cb70-1" tabindex="-1"></a>vec_arith.vctrs_meter.MISSING <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb70-2"><a href="#cb70-2" tabindex="-1"></a> <span class="cf">switch</span>(op,</span>
<span id="cb70-3"><a href="#cb70-3" tabindex="-1"></a> <span class="st">`</span><span class="at">-</span><span class="st">`</span> <span class="ot">=</span> x <span class="sc">*</span> <span class="sc">-</span><span class="dv">1</span>,</span>
<span id="cb70-4"><a href="#cb70-4" tabindex="-1"></a> <span class="st">`</span><span class="at">+</span><span class="st">`</span> <span class="ot">=</span> x,</span>
<span id="cb70-5"><a href="#cb70-5" tabindex="-1"></a> <span class="fu">stop_incompatible_op</span>(op, x, y)</span>
<span id="cb70-6"><a href="#cb70-6" tabindex="-1"></a> )</span>
<span id="cb70-7"><a href="#cb70-7" tabindex="-1"></a>}</span>
<span id="cb70-8"><a href="#cb70-8" tabindex="-1"></a><span class="sc">-</span><span class="fu">meter</span>(<span class="dv">1</span>)</span>
<span id="cb70-9"><a href="#cb70-9" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb70-10"><a href="#cb70-10" tabindex="-1"></a><span class="co">#&gt; [1] -1 m</span></span>
<span id="cb70-11"><a href="#cb70-11" tabindex="-1"></a><span class="sc">+</span><span class="fu">meter</span>(<span class="dv">1</span>)</span>
<span id="cb70-12"><a href="#cb70-12" tabindex="-1"></a><span class="co">#&gt; &lt;vctrs_meter[1]&gt;</span></span>
<span id="cb70-13"><a href="#cb70-13" tabindex="-1"></a><span class="co">#&gt; [1] 1 m</span></span></code></pre></div>
</div>
</div>
<div id="implementing-a-vctrs-s3-class-in-a-package" class="section level2">
<h2>Implementing a vctrs S3 class in a package</h2>
<p>Defining S3 methods interactively is fine for iteration and
exploration, but if your class lives in a package, you need to do a few
more things:</p>
<ul>
<li><p>Register the S3 methods by listing them in the
<code>NAMESPACE</code> file.</p></li>
<li><p>Create documentation around your methods, for the sake of your
user and to satisfy <code>R CMD check</code>.</p></li>
</ul>
<p>Lets assume that the <code>percent</code> class is implemented in
the pizza package in the file <code>R/percent.R</code>. Here we walk
through the major sections of this hypothetical file. Youve seen all of
this code before, but now its augmented by the roxygen2 directives that
produce the correct <code>NAMESPACE</code> entries and help topics.</p>
<div id="getting-started" class="section level3">
<h3>Getting started</h3>
<p>First, the pizza package needs to include vctrs in the
<code>Imports</code> section of its <code>DESCRIPTION</code> (perhaps by
calling <code>usethis::use_package(&quot;vctrs&quot;)</code>. While vctrs is under
very active development, it probably makes sense to state a minimum
version.</p>
<pre><code>Imports:
a_package,
another_package,
...
vctrs (&gt;= x.y.z),
...</code></pre>
<p>Then we make all vctrs functions available within the pizza package
by including the directive <code>#&#39; @import vctrs</code> somewhere.
Usually, its not good practice to <code>@import</code> the entire
namespace of a package, but vctrs is deliberately designed with this use
case in mind.</p>
<p>Where should we put <code>#&#39; @import vctrs</code>? There are two
natural locations:</p>
<ul>
<li><p>With package-level docs in <code>R/pizza-doc.R</code>. You can
use <code>usethis::use_package_doc()</code> to initiate this
package-level documentation.</p></li>
<li><p>In <code>R/percent.R</code>. This makes the most sense when the
vctrs S3 class is a modest and self-contained part of the overall
package.</p></li>
</ul>
<p>We also must use one of these locations to dump some internal
documentation thats needed to avoid <code>R CMD check</code>
complaints. We dont expect any human to ever read this documentation.
Heres how this dummy documentation should look, combined with the
<code>#&#39; @import vctrs</code> directive described above.</p>
<div class="sourceCode" id="cb72"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb72-1"><a href="#cb72-1" tabindex="-1"></a><span class="co">#&#39; Internal vctrs methods</span></span>
<span id="cb72-2"><a href="#cb72-2" tabindex="-1"></a><span class="co">#&#39;</span></span>
<span id="cb72-3"><a href="#cb72-3" tabindex="-1"></a><span class="co">#&#39; @import vctrs</span></span>
<span id="cb72-4"><a href="#cb72-4" tabindex="-1"></a><span class="co">#&#39; @keywords internal</span></span>
<span id="cb72-5"><a href="#cb72-5" tabindex="-1"></a><span class="co">#&#39; @name pizza-vctrs</span></span>
<span id="cb72-6"><a href="#cb72-6" tabindex="-1"></a><span class="cn">NULL</span></span></code></pre></div>
<p>This should appear in <code>R/pizza-doc.R</code> (package-level docs)
or in <code>R/percent.R</code> (class-focused file).</p>
<p>Remember to call <code>devtools::document()</code> regularly, as you
develop, to regenerate <code>NAMESPACE</code> and the <code>.Rd</code>
files.</p>
<p>From this point on, the code shown is expected to appear in
<code>R/percent.R</code>.</p>
</div>
<div id="low-level-and-user-friendly-constructors" class="section level3">
<h3>Low-level and user-friendly constructors</h3>
<p>Next we add our constructor:</p>
<div class="sourceCode" id="cb73"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb73-1"><a href="#cb73-1" tabindex="-1"></a>new_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">x =</span> <span class="fu">double</span>()) {</span>
<span id="cb73-2"><a href="#cb73-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="sc">!</span><span class="fu">is_double</span>(x)) {</span>
<span id="cb73-3"><a href="#cb73-3" tabindex="-1"></a> <span class="fu">abort</span>(<span class="st">&quot;`x` must be a double vector.&quot;</span>)</span>
<span id="cb73-4"><a href="#cb73-4" tabindex="-1"></a> }</span>
<span id="cb73-5"><a href="#cb73-5" tabindex="-1"></a> <span class="fu">new_vctr</span>(x, <span class="at">class =</span> <span class="st">&quot;pizza_percent&quot;</span>)</span>
<span id="cb73-6"><a href="#cb73-6" tabindex="-1"></a>}</span></code></pre></div>
<p>Note that the name of the package must be included in the class name
(<code>pizza_percent</code>), but it does not need to be included in the
constructor name. You do not need to export the constructor, unless you
want people to extend your class.</p>
<p>We can also add a call to <code>setOldClass()</code> for
compatibility with S4:</p>
<div class="sourceCode" id="cb74"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb74-1"><a href="#cb74-1" tabindex="-1"></a><span class="co"># for compatibility with the S4 system</span></span>
<span id="cb74-2"><a href="#cb74-2" tabindex="-1"></a>methods<span class="sc">::</span><span class="fu">setOldClass</span>(<span class="fu">c</span>(<span class="st">&quot;pizza_percent&quot;</span>, <span class="st">&quot;vctrs_vctr&quot;</span>))</span></code></pre></div>
<p>Because weve used a function from the methods package, youll also
need to add methods to <code>Imports</code>, with (e.g.)
<code>usethis::use_package(&quot;methods&quot;)</code>. This is a “free”
dependency because methods is bundled with every R install.</p>
<p>Next we implement, export, and document a user-friendly helper:
<code>percent()</code>.</p>
<div class="sourceCode" id="cb75"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb75-1"><a href="#cb75-1" tabindex="-1"></a><span class="co">#&#39; `percent` vector</span></span>
<span id="cb75-2"><a href="#cb75-2" tabindex="-1"></a><span class="co">#&#39;</span></span>
<span id="cb75-3"><a href="#cb75-3" tabindex="-1"></a><span class="co">#&#39; This creates a double vector that represents percentages so when it is</span></span>
<span id="cb75-4"><a href="#cb75-4" tabindex="-1"></a><span class="co">#&#39; printed, it is multiplied by 100 and suffixed with `%`.</span></span>
<span id="cb75-5"><a href="#cb75-5" tabindex="-1"></a><span class="co">#&#39;</span></span>
<span id="cb75-6"><a href="#cb75-6" tabindex="-1"></a><span class="co">#&#39; @param x A numeric vector</span></span>
<span id="cb75-7"><a href="#cb75-7" tabindex="-1"></a><span class="co">#&#39; @return An S3 vector of class `pizza_percent`.</span></span>
<span id="cb75-8"><a href="#cb75-8" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb75-9"><a href="#cb75-9" tabindex="-1"></a><span class="co">#&#39; @examples</span></span>
<span id="cb75-10"><a href="#cb75-10" tabindex="-1"></a><span class="co">#&#39; percent(c(0.25, 0.5, 0.75))</span></span>
<span id="cb75-11"><a href="#cb75-11" tabindex="-1"></a>percent <span class="ot">&lt;-</span> <span class="cf">function</span>(<span class="at">x =</span> <span class="fu">double</span>()) {</span>
<span id="cb75-12"><a href="#cb75-12" tabindex="-1"></a> x <span class="ot">&lt;-</span> <span class="fu">vec_cast</span>(x, <span class="fu">double</span>())</span>
<span id="cb75-13"><a href="#cb75-13" tabindex="-1"></a> <span class="fu">new_percent</span>(x)</span>
<span id="cb75-14"><a href="#cb75-14" tabindex="-1"></a>}</span></code></pre></div>
<p>(Again note that the package name will appear in the class, but does
not need to occur in the function, because we can already do
<code>pizza::percent()</code>; it would be redundant to have
<code>pizza::pizza_percent()</code>.)</p>
</div>
<div id="other-helpers" class="section level3">
<h3>Other helpers</h3>
<p>Its a good idea to provide a function that tests if an object is of
this class. If you do so, it makes sense to document it with the
user-friendly constructor <code>percent()</code>:</p>
<div class="sourceCode" id="cb76"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb76-1"><a href="#cb76-1" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb76-2"><a href="#cb76-2" tabindex="-1"></a><span class="co">#&#39; @rdname percent</span></span>
<span id="cb76-3"><a href="#cb76-3" tabindex="-1"></a>is_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x) {</span>
<span id="cb76-4"><a href="#cb76-4" tabindex="-1"></a> <span class="fu">inherits</span>(x, <span class="st">&quot;pizza_percent&quot;</span>)</span>
<span id="cb76-5"><a href="#cb76-5" tabindex="-1"></a>}</span></code></pre></div>
<p>Youll also need to update the <code>percent()</code> documentation
to reflect that <code>x</code> now means two different things:</p>
<div class="sourceCode" id="cb77"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb77-1"><a href="#cb77-1" tabindex="-1"></a><span class="co">#&#39; @param x</span></span>
<span id="cb77-2"><a href="#cb77-2" tabindex="-1"></a><span class="co">#&#39; * For `percent()`: A numeric vector</span></span>
<span id="cb77-3"><a href="#cb77-3" tabindex="-1"></a><span class="co">#&#39; * For `is_percent()`: An object to test.</span></span></code></pre></div>
<p>Next we provide the key methods to make printing work. These are S3
methods, so they dont need to be documented, but they do need to be
exported.</p>
<div class="sourceCode" id="cb78"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb78-1"><a href="#cb78-1" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb78-2"><a href="#cb78-2" tabindex="-1"></a>format.pizza_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb78-3"><a href="#cb78-3" tabindex="-1"></a> out <span class="ot">&lt;-</span> <span class="fu">formatC</span>(<span class="fu">signif</span>(<span class="fu">vec_data</span>(x) <span class="sc">*</span> <span class="dv">100</span>, <span class="dv">3</span>))</span>
<span id="cb78-4"><a href="#cb78-4" tabindex="-1"></a> out[<span class="fu">is.na</span>(x)] <span class="ot">&lt;-</span> <span class="cn">NA</span></span>
<span id="cb78-5"><a href="#cb78-5" tabindex="-1"></a> out[<span class="sc">!</span><span class="fu">is.na</span>(x)] <span class="ot">&lt;-</span> <span class="fu">paste0</span>(out[<span class="sc">!</span><span class="fu">is.na</span>(x)], <span class="st">&quot;%&quot;</span>)</span>
<span id="cb78-6"><a href="#cb78-6" tabindex="-1"></a> out</span>
<span id="cb78-7"><a href="#cb78-7" tabindex="-1"></a>}</span>
<span id="cb78-8"><a href="#cb78-8" tabindex="-1"></a></span>
<span id="cb78-9"><a href="#cb78-9" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb78-10"><a href="#cb78-10" tabindex="-1"></a>vec_ptype_abbr.pizza_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, ...) {</span>
<span id="cb78-11"><a href="#cb78-11" tabindex="-1"></a> <span class="st">&quot;prcnt&quot;</span></span>
<span id="cb78-12"><a href="#cb78-12" tabindex="-1"></a>}</span></code></pre></div>
<p>Finally, we implement methods for <code>vec_ptype2()</code> and
<code>vec_cast()</code>.</p>
<div class="sourceCode" id="cb79"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb79-1"><a href="#cb79-1" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb79-2"><a href="#cb79-2" tabindex="-1"></a>vec_ptype2.vctrs_percent.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">new_percent</span>()</span>
<span id="cb79-3"><a href="#cb79-3" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb79-4"><a href="#cb79-4" tabindex="-1"></a>vec_ptype2.double.vctrs_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, y, ...) <span class="fu">double</span>()</span>
<span id="cb79-5"><a href="#cb79-5" tabindex="-1"></a></span>
<span id="cb79-6"><a href="#cb79-6" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb79-7"><a href="#cb79-7" tabindex="-1"></a>vec_cast.pizza_percent.pizza_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) x</span>
<span id="cb79-8"><a href="#cb79-8" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb79-9"><a href="#cb79-9" tabindex="-1"></a>vec_cast.pizza_percent.double <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">percent</span>(x)</span>
<span id="cb79-10"><a href="#cb79-10" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb79-11"><a href="#cb79-11" tabindex="-1"></a>vec_cast.double.pizza_percent <span class="ot">&lt;-</span> <span class="cf">function</span>(x, to, ...) <span class="fu">vec_data</span>(x)</span></code></pre></div>
</div>
<div id="arithmetic-1" class="section level3">
<h3>Arithmetic</h3>
<p>Writing double dispatch methods for <code>vec_arith()</code> is
currently more awkward than writing them for <code>vec_ptype2()</code>
or <code>vec_cast()</code>. We plan to improve this in the future. For
now, you can use the following instructions.</p>
<p>If you define a new type and want to write <code>vec_arith()</code>
methods for it, youll need to provide a new single dispatch S3 generic
for it of the following form:</p>
<div class="sourceCode" id="cb80"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb80-1"><a href="#cb80-1" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb80-2"><a href="#cb80-2" tabindex="-1"></a><span class="co">#&#39; @method vec_arith my_type</span></span>
<span id="cb80-3"><a href="#cb80-3" tabindex="-1"></a>vec_arith.my_type <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb80-4"><a href="#cb80-4" tabindex="-1"></a> <span class="fu">UseMethod</span>(<span class="st">&quot;vec_arith.my_type&quot;</span>, y)</span>
<span id="cb80-5"><a href="#cb80-5" tabindex="-1"></a>}</span></code></pre></div>
<p>Note that this actually functions as both an S3 method for
<code>vec_arith()</code> and an S3 generic called
<code>vec_arith.my_type()</code> that dispatches off <code>y</code>.
roxygen2 only recognizes it as an S3 generic, so you have to register
the S3 method part of this with an explicit <code>@method</code>
call.</p>
<p>After that, you can define double dispatch methods, but you still
need an explicit <code>@method</code> tag to ensure it is registered
with the correct generic:</p>
<div class="sourceCode" id="cb81"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb81-1"><a href="#cb81-1" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb81-2"><a href="#cb81-2" tabindex="-1"></a><span class="co">#&#39; @method vec_arith.my_type my_type</span></span>
<span id="cb81-3"><a href="#cb81-3" tabindex="-1"></a>vec_arith.my_type.my_type <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb81-4"><a href="#cb81-4" tabindex="-1"></a> <span class="co"># implementation here</span></span>
<span id="cb81-5"><a href="#cb81-5" tabindex="-1"></a>}</span>
<span id="cb81-6"><a href="#cb81-6" tabindex="-1"></a></span>
<span id="cb81-7"><a href="#cb81-7" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb81-8"><a href="#cb81-8" tabindex="-1"></a><span class="co">#&#39; @method vec_arith.my_type integer</span></span>
<span id="cb81-9"><a href="#cb81-9" tabindex="-1"></a>vec_arith.my_type.integer <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb81-10"><a href="#cb81-10" tabindex="-1"></a> <span class="co"># implementation here</span></span>
<span id="cb81-11"><a href="#cb81-11" tabindex="-1"></a>}</span>
<span id="cb81-12"><a href="#cb81-12" tabindex="-1"></a></span>
<span id="cb81-13"><a href="#cb81-13" tabindex="-1"></a><span class="co">#&#39; @export</span></span>
<span id="cb81-14"><a href="#cb81-14" tabindex="-1"></a><span class="co">#&#39; @method vec_arith.integer my_type</span></span>
<span id="cb81-15"><a href="#cb81-15" tabindex="-1"></a>vec_arith.integer.my_type <span class="ot">&lt;-</span> <span class="cf">function</span>(op, x, y, ...) {</span>
<span id="cb81-16"><a href="#cb81-16" tabindex="-1"></a> <span class="co"># implementation here</span></span>
<span id="cb81-17"><a href="#cb81-17" tabindex="-1"></a>}</span></code></pre></div>
<p>vctrs provides the hybrid S3 generics/methods for most of the base R
types, like <code>vec_arith.integer()</code>. If you dont fully import
vctrs with <code>@import vctrs</code>, then you will need to explicitly
import the generic you are registering double dispatch methods for with
<code>@importFrom vctrs vec_arith.integer</code>.</p>
</div>
<div id="testing" class="section level3">
<h3>Testing</h3>
<p>Its good practice to test your new class. Specific
recommendations:</p>
<ul>
<li><p><code>R/percent.R</code> is the type of file where you really do
want 100% test coverage. You can use
<code>devtools::test_coverage_file()</code> to check this.</p></li>
<li><p>Make sure to test behaviour with zero-length inputs and missing
values.</p></li>
<li><p>Use <code>testthat::verify_output()</code> to test your format
method. Customised printing is often a primary motivation for creating
your own S3 class in the first place, so this will alert you to
unexpected changes in your printed output. Read more about
<code>verify_output()</code> in the <a href="https://www.tidyverse.org/blog/2019/11/testthat-2-3-0/">testthat
v2.3.0 blog post</a>; its an example of a so-called <a href="https://ro-che.info/articles/2017-12-04-golden-tests">golden
test</a>.</p></li>
<li><p>Check for method symmetry; use <code>expect_s3_class()</code>,
probably with <code>exact = TRUE</code>, to ensure that
<code>vec_c(x, y)</code> and <code>vec_c(y, x)</code> return the same
type of output for the important <code>x</code>s and <code>y</code>s in
your domain.</p></li>
<li><p>Use <code>testthat::expect_error()</code> to check that inputs
that cant be combined fail with an error. Here, you should be generally
checking the class of the error, not its message. Relevant classes
include <code>vctrs_error_assert_ptype</code>,
<code>vctrs_error_assert_size</code>, and
<code>vctrs_error_incompatible_type</code>.</p>
<div class="sourceCode" id="cb82"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb82-1"><a href="#cb82-1" tabindex="-1"></a><span class="fu">expect_error</span>(<span class="fu">vec_c</span>(<span class="dv">1</span>, <span class="st">&quot;a&quot;</span>), <span class="at">class =</span> <span class="st">&quot;vctrs_error_incompatible_type&quot;</span>)</span></code></pre></div></li>
</ul>
<p>If your tests pass when run by <code>devtools::test()</code>, but
fail when run in <code>R CMD check</code>, it is very likely to reflect
a problem with S3 method registration. Carefully check your roxygen2
comments and the generated <code>NAMESPACE</code>.</p>
</div>
<div id="existing-classes" class="section level3">
<h3>Existing classes</h3>
<p>Before you build your own class, you might want to consider using, or
subclassing existing classes. You can check <a href="https://github.com/krlmlr/awesome-vctrs">awesome-vctrs</a> for a
curated list of R vector classes, some of which are built with
vctrs.</p>
<p>If youve built or extended a class, consider adding it to that list
so other people can use it.</p>
</div>
</div>
<!-- code folding -->
<!-- dynamically load mathjax for compatibility with self-contained -->
<script>
(function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
document.getElementsByTagName("head")[0].appendChild(script);
})();
</script>
</body>
</html>