| print {mvbutils} | R Documentation |
Print values
Description
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.
Usage
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)
Arguments
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 |
Value
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.
How to override an s3 method
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
S3methodin themypackNAMESPACE file (whichmvbutilspackaging 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
S3methodin the NAMESPACE file ofmypack. 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 formypack, where the new generic lives). The new generic will probably be identical to the existing generic, very likely just a call toUseMethod. 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.help2flatdocis useful here, assuming you're wisely usingmvbutilsto 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 ofUseMethodand 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 inasNamespace(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.defaultmethod inmypack— and you need to export and therefore document it too, as per the previous point. Thisgeneric.defaultjust 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. Seemvbutils:::print.defaultfor how to do it. If you were redefining a generic that was originally (or most recently) defined somewhere other thanbaseenv(), then you'd need to replace the latter withasNamespace(<<original.defining.package>>).Because your new
generic.defaultmight invoke any of the pre-existing (or subsequently-registered) methods of the original generic, you should just make its argument listx,.... In other words, don't name individual arguments even if they are named in the originalgeneric.default(eg forprint.default).Objects of mode
name,call, and"("or"{"or"<-"(special types ofcall) cause trouble ingeneric.default(at least using the approach in the previous point, as inmvbutils:::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 acallobject, you'll need a specialgeneric.callmethod, as inmvbutils:::print.call; the same goes for those other objects.A few generics—
rbindandcbind, for example— use their own internal dispatch mechanism and don't have e.g. anrbind.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 forrbind, 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 inasNamespace("mypack"), whether defined inmypackitself or subsequently registered by another package that usesmypack, (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 importmypack), then step (1) is skipped. This is good; if another package pack2 importsmypackand registers an S3 method, the S3 registration will go into themypackS3 lookup table, but ifpack2doesn't importmypackthen 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).
Examples
## 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)