<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/why-your-s3-method-isnt-working/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://edwinth.github.io/blog/s3-generics/"> That’s so Random</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>Throughout the last years I noticed the following happening with a number of people. One of those people was actually yours truely a few years back. Person is aware of S3 methods in R through regular use of <code>print</code>, <code>plot</code> and <code>summary</code> functions and decides to give it a go in own work. Creates a function that assigns a class to its output and then implements a bunch of methods to work on the class. Strangely, some of these methods appear to be working as expected, while others throw an error. After a confusing and painful debugging session, person throws hands in the air and continues working without S3 methods. Which was working fine in the first place. This is a real pity, because all the person is overlooking is a very small step in the S3 chain: the generic function.</p>
<h2>A nonworking method</h2>
<p>So we have a function doing all kinds of complicated stuff. It outputs a list with several elements. We assign a S3 class to it before returning, so we can subsequently implement a number of methods<sup><a rel="nofollow" href="https://edwinth.github.io/blog/s3-generics/#fn:1" target="_blank">1</a></sup>. Lets just make something up here.</p>
<div>
<div>
<pre><code><span>my_func</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>x</span><span>)</span><span> </span><span>{</span><span>
</span><span>ret</span><span> </span><span>&lt;-</span><span> </span><span>list</span><span>(</span><span>dataset</span><span> </span><span>=</span><span> </span><span>x</span><span>,</span><span> </span><span>d</span><span> </span><span>=</span><span> </span><span>42</span><span>,</span><span> </span><span>y</span><span> </span><span>=</span><span> </span><span>rnorm</span><span>(</span><span>10</span><span>),</span><span> </span><span>z</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>'a'</span><span>,</span><span> </span><span>'b'</span><span>,</span><span> </span><span>'a'</span><span>,</span><span> </span><span>'c'</span><span>))</span><span>
</span><span>class</span><span>(</span><span>ret</span><span>)</span><span> </span><span>&lt;-</span><span> </span><span>&quot;myS3&quot;</span><span>
</span><span>ret</span><span>
</span><span>}</span><span>
</span><span>out</span><span> </span><span>&lt;-</span><span> </span><span>my_func</span><span>(</span><span>mtcars</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p>Perfect, now lets implement a <code>print</code> method. Rather than outputting the whole list, we just want to know the most vital information when printing.</p>
<div>
<div>
<pre><code><span>print.myS3</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>x</span><span>)</span><span> </span><span>{</span><span>
</span><span>cat</span><span>(</span><span>&quot;Original dataset has&quot;</span><span>,</span><span> </span><span>nrow</span><span>(</span><span>x</span><span>$</span><span>dataset</span><span>),</span><span> </span><span>&quot;rows and&quot;</span><span>,</span><span>
</span><span>ncol</span><span>(</span><span>x</span><span>$</span><span>dataset</span><span>),</span><span> </span><span>&quot;columns\n&quot;</span><span>,</span><span> </span><span>&quot;d is&quot;</span><span>,</span><span> </span><span>x</span><span>$</span><span>d</span><span>)</span><span>
</span><span>}</span><span>
</span><span>out</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## Original dataset has 32 rows and 11 columns
##  d is 42
</code></pre>
</div>
</div>
<p>Ha, that is working!. Now we do a <code>mean</code> method, that gives us the mean of the <code>y</code> variable.</p>
<div>
<div>
<pre><code><span>mean.myS3</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>x</span><span>)</span><span> </span><span>{</span><span>
</span><span>mean</span><span>(</span><span>x</span><span>$</span><span>y</span><span>)</span><span>
</span><span>}</span><span>
</span><span>mean</span><span>(</span><span>out</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## [1] 0.2631094
</code></pre>
</div>
</div>
<p>Works too! And finally we do a <code>count_letters</code> method. It takes <code>z</code> from the output and counts how often each letter occurs.</p>
<div>
<div>
<pre><code><span>count_letters.myS3</span><span> </span><span>&lt;-</span><span> </span><span>function</span><span>(</span><span>x</span><span>)</span><span> </span><span>{</span><span>
</span><span>table</span><span>(</span><span>out</span><span>$</span><span>z</span><span>)</span><span>
</span><span>}</span><span>
</span><span>count_letters</span><span>(</span><span>out</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## Error in count_letters(out): could not find function &quot;count_letters&quot;
</code></pre>
</div>
</div>
<p>What do you mean “could not find function”? It is right there! Maybe we made a typo. Mmmm, no it doesn’t seem so. Maybe, mmmm, lets look into this…. Half an hour, a bit of swearing and feelings of stupidity later. Pfff, lets not bother about S3, we were happy with just using functions in the first place.</p>
<h2>Generics</h2>
<p>Now why are <code>print</code> and <code>mean</code> working just fine, but <code>count_letters</code> isn’t? Lets look under the hood of <code>print</code> and <code>mean</code>.</p>
<div>
<div>
<pre><code><span>print</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## function (x, ...) 
## UseMethod(&quot;print&quot;)
## </code></pre></div></div>