Description Usage Arguments Value How to override an s3 method Examples
See base-R documentation of print
and print.default
. Users should see no difference with the mvbutils
versions; they need to be documented and exported in mvbutils
for purely technical reasons. There are also three useful special-purpose print methods in mvbutils
; see Value.Some of the base-R documentation is reproduced below.
The motive for redeclaration is to have a seamless transition within the fixr
editing system, from the nice simple "source"-attribute system used to store function source-code before R2.14, to the quite extraordinarily complicated "srcref" system used thereafter. mvbutils
does so via an augmented version of base-R's print method for functions, without which your careful formatting and commenting would be lost. If a function has a "source" attribute but no "srcref" attribute (as would be the case for many functions created prior to R2.14), then the augmented print.function
will use the "source" attribute. There is no difference from base-R in normal use.
See How to override an s3 method if you really want to understand the technicalities.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | print(x, ...) # generic
## Default S3 method:
print(x, ...) # S3 method for default
## S3 method for class 'function'
print(x, useSource=TRUE, ...) # S3 method for function
## S3 method for class 'cat'
print(x, ...) # S3 method for cat
## S3 method for class 'specialprint'
print(x, ...) # S3 method for specialprint
## S3 method for class 'pagertemp'
print(x, ...) # S3 method for pagertemp
## S3 method for class 'call'
print(x, ...) # S3 method for call
## S3 replacement method for class ''<-''
print(x, ...) # S3 method for "<-" (a special sort of call)
## S3 method for class ''(''
print(x, ...) # S3 method for "(" (a special sort of call)
#print(x, ...) # S3 method for "{" (a special sort of call)
## S3 method for class ''if''
print(x, ...) # S3 method for "if" (a special sort of call)
## S3 method for class ''for''
print(x, ...) # S3 method for "for" (a special sort of call)
## S3 method for class ''while''
print(x, ...) # S3 method for "while" (a special sort of call)
## S3 method for class 'name'
print(x, ...) # S3 method for name (symbol)
|
x |
thing to print. |
... |
other arguments passed to |
useSource |
[print.function] logical, indicating whether to use source references or copies rather than deparsing language objects. The default is to use the original source if it is available. The |
Technically, an invisible
version of the object is returned. But the point of print
is to display the object. print.function
displays source code, as per Description. print.default
and print.call
need to exist in mvbutils
only for technical reasons. The other two special methods are:
print.cat
applies to character vectors of S3 class cat
, which are printed each on a new line, without the [1] prefix or double-quotes or backslashes. It's ideal for displaying "plain text". Use as.cat
to coerce a character vector so that it prints this way.
print.specialprint
can be used to ensure an object (of class specialprint
) displays in any particular way you want, without bothering to define a new S3 class and write a print method. Just give the object an attribute "print" of mode expression
, which can refer to the main argument x
and any other arguments. That expression will be run by print.specialprint
– see Examples.
print.pagertemp
is meant only for internal use by the informal-help viewer.
Suppose you maintain a package mypack in which you want to mildly redefine an existing S3 method, like mvbutils
does with print.function
. (Drastic redefinitions are likely to be a bad idea, but adding or tweaking functionality can occasionally make sense.) The aim is that other packages which import mypack
should end up using your redefined method, and so should the user if they have explicitly called library( mypack)
. But your redefined method should not be visible to packages that don't import mypack
, nor to the user if mypack
has only been loaded implicitly (i.e. if mypack
is imported by another package, so that asNamespace(mypack)
is loaded but package:mypack
doesn't appear on the search path). It's hard to find out how to do this. Here's what I have discovered:
For a new S3 method (i.e. for a class that doesn't already have one), then you just need to mark it as an S3method
in the mypack
NAMESPACE file (which mvbutils
packaging tools do for you automatically). You don't need to document the new method explicitly, and consequently there's no need to export it. The new method will still be found when the generic runs on an object of the appropriate class.
If you're modifying an existing method, you can't just declare it as S3method
in the NAMESPACE file of mypack
. If that's all you did, R would complain that it already has a registered method for that class— fair enough. Therefore, you also have to redeclare and export the generic, so that there's a "clean slate" for registering the method (specifically, in the S3 methods table for mypack
, where the new generic lives). The new generic will probably be identical to the existing generic, very likely just a call to UseMethod
. Because it's exported, it needs to be documented; you can either just refer to base-R documentation (but you still need all the formal stuff for Arguments etc, otherwise RCMD CHECK complains), or you can duplicate the base-R doco with a note. help2flatdoc
is useful here, assuming you're wisely using mvbutils
to build & maintain your package.
If you redeclare the generic, you also need to make sure that your method is exported as well as S3-registered in the NAMESPACE file of mypack
. This is somehow connected with the obscure scoping behaviour of UseMethod
and I don't really understand it, but the result is: if you don't export your method, then it's not found by the new generic (even though it exists in asNamespace(mypack)
, which is the environment of the new generic, and even though your method is also S3-registered in that same environment). Because you export the method, you also need to document it.
Unfortunately, the new generic won't know about the methods already registered for the old generic. So, for most generics (exceptions listed later), you will also have to define a generic.default
method in mypack
— and you need to export and therefore document it too, as per the previous point. This generic.default
just needs to invoke the original generic, so that the already-registered S3 methods are searched. However, this can lead to infinite loops if you're not careful. See mvbutils:::print.default
for how to do it. If you were redefining a generic that was originally (or most recently) defined somewhere other than baseenv()
, then you'd need to replace the latter with asNamespace(<<original.defining.package>>)
.
Because your new generic.default
might invoke any of the pre-existing (or subsequently-registered) methods of the original generic, you should just make its argument list x,...
. In other words, don't name individual arguments even if they are named in the original generic.default
(eg for print.default
).
Objects of mode name
, call
, and "("
or "{"
or "<-"
(special types of call
) cause trouble in generic.default
(at least using the approach in the previous point, as in mvbutils:::print.default
). Unless they have a specific method, the object will be automatically evaluated. So if your generic is ever likely to be invoked on a call
object, you'll need a special generic.call
method, as in mvbutils:::print.call
; the same goes for those other objects.
A few generics— rbind
and cbind
, for example— use their own internal dispatch mechanism and don't have e.g. an rbind.default
. Of course, there is a default behaviour, but it's not defined by an R-level function; see ?InternalGenerics
. For these generics, the previous point wouldn't work as a way of looking for existing methods. Fortunately, at least for rbind
, things seem to "just work" if your redefined generic simply runs the code of the base generic (but don't call the latter directly, or you risk infinite loops— just run its body code). Then, if your generic is run, the search order is (1) methods registered for your generic in asNamespace("mypack")
, whether defined in mypack
itself or subsequently registered by another package that uses mypack
, (2) methods defined/registered for the base generic (ie in the original generic's namespace), (3) the original "implicit default method". But if the original generic is run (e.g. from another package that doesn't import mypack
), then step (1) is skipped. This is good; if another package pack2 imports mypack
and registers an S3 method, the S3 registration will go into the mypack
S3 lookup table, but if pack2
doesn't import mypack
then the S3 registration will go into the base S3 lookup table (or the lookup table for whichever package the generic was originally defined in, eg package stats).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ## Not run:
# Special methods shown below; basic behaviour of 'print', 'print.default',
# and 'print.function' is as for base-R
#cat
ugly.bugly <- c( 'A rose by any other name', 'would annoy taxonomists')
ugly.bugly
#[1] "A rose by any other name" "would annoy taxonomists"
as.cat( ugly.bugly) # calls print.cat--- no clutter
#A rose by any other name
#would annoy taxonomists
# nullprint:
biggo <- 1:1000
biggo
# [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# [2] 19 20 21 22 23 24 25 26 27 28 etc...
oldClass( biggo) <- 'nullprint'
biggo # calls print.nullprint
# nuthin'
# specialprint:
x <- matrix( exp( seq( -20, 19, by=2)), 4, 5)
attr( x, 'print') <- expression( {
x[] <- sprintf( '%12.2f', x);
class( x) <- 'noquote';
attr( x, 'print') <- NULL;
print( x)
})
class( x) <- 'specialprint'
x # calls print.specialprint; consistently formatted for once
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0.00 0.00 0.02 54.60 162754.79
#[2,] 0.00 0.00 0.14 403.43 1202604.28
#[3,] 0.00 0.00 1.00 2980.96 8886110.52
#[4,] 0.00 0.00 7.39 22026.47 65659969.14
## End(Not run)
|
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.