[Roxygen-devel] S4 implementation of @usage

Hadley Wickham hadley at rice.edu
Wed Aug 29 19:25:19 CEST 2012


On Wed, Aug 29, 2012 at 12:00 PM, Vitalie Spinu <spinuvit at gmail.com> wrote:
>   >> Hadley Wickham <hadley at rice.edu>
>   >> on Wed, 29 Aug 2012 09:07:41 -0500 wrote:
>
>   > Hi Vitalie,
>   > It would also be really useful if you could sketch out the S4 design
>   > to work with a tag like usage.
>
>   > Would you need multiple dispatch so that usage looks like:
>
>   > setMethod("ProcessTag", signature(tag = "RoccerUsage", object =
>   > "function"), ...)
>   > setMethod("ProcessTag", signature(tag = "RoccerUsage", object =
>   > "classRepresentation"), ...)
>   > setMethod("ProcessTag", signature(tag = "RoccerUsage", object =
>   > "MethodDefinition"), ...)
>
> Yes, this is precisely what I had in mind. A couple of comments.
>
>  1)  Its a convention in R that class names are CamelCase (as you did) but
>  method names are CamelCase which start with small letter (processTag).

Oh, yeah, it'll take my java typing skills a while to come back ;)

>  It's a very nice convention, especially with
>  functions_separated_by_underscors conventions which you extensively
>  use. Code becomes very easy to read, as you know what is method, class
>  and where is a simple function or object.
>
>  2) I think processTag should have 4 arguments (though I might be not
>  seeing the whole picture). The third argument being RocBlock (or RocDoc?) -- an
>  object containing all the tags of the current documentation chunk and
>  an object. The forth being RocPackage containing all the RocDoc objects
>  collected during the evaluation. The processTag method for a specific
>  tag need not use the last two. RocBlock is needed, because the tag
>  processing might depend on other tags in the same documentation chunk.

So you'd have:

* roccer, a Roccer
* rocblock, a RocBlock
* rocpackage, a RocPackage (i.e. a list of RocBlocks)

and

* object

Object seems a bit awkward because it's part of the rocblock, and is
currently stored in a list with it's value and it's name. But I don't
see how you'd dispatch on it, unless you pull it out into a separate
argument.

>  No need to dispatch on the last 2 arguments as far. May be in the
>  future when you will have different types of RocBlocks and RocPackage
>  (oxigen, markdown etc.).

Agreed.

>  The question is how you make tags "communicate" to each other (global
>  tags like family or inheritParams). One option is preParse stage (which
>  I think you are doing right now). Another one, is to add one special
>  slot to global tags which is a common environment. Then this special
>  tags can communicate to each other using that environment.

I find the approach of having a pre-parsing phase much easier to test,
which is why I generally prefer it.

>   > Is that better than this? (effectively the same as the current s3
>   > implementation)
>
>   > setMethod("ProccessTag", signature(tag = "RoccerUsage"), function(tag, obj) {
>   >   makeUsageObject(obj at value, obj at name)
>   > }
>   > setMethod("makeUsageObject", signature(object = "function"), ...)
>   > setMethod("makeUsageObject", signature(object = "classRepresentation"), ...)
>   > setMethod("makeUsageObject", signature(object = "MethodDefinition"), ...)
>
> This is no good, for each Tag you create a method. The main thing about
> methods is that you can have one processTag and that works for all tags.

But it would make the arguments to processTag much cleaner.

>   > Currently the roccer implements the strategy design pattern so that
>   > you can separately specify the parser and the output. How would you do
>   > the same with S4?  Multiple inheritance?
>
> This are methods for each tag. You must have rocParse method and rocRd
> method, that's all. Not rocOut, you will pretty soon have rocHTHL,
> rocMarkdown etc to output into other formats. (maybe outputRd,
> outputHTML etc).

Hmmm, so to define a new type of output, you define a new generic like
OutRd, OutNamespace,  OutDescription etc.  The methods would then
return objects that give information so that the Out object can put
them in the right class. For Rd this is a file name and some rd
commands, for namespace it's a character vector of lines, for
description it's named fields (probably just strings).

>   > setClass("RoccerUsage", contains = c("RoccerParseSingle",
>   > "RoccerOutRd"))
>
> This is not good. Tag class is an entity to represent an abstract and
> isolated notion of a tag concept. It should not have anything to do with
> the output output, parsing, printing etc. All of the actions which one
> might apply to an object should be implemented as methods. I guess you
> are still thinking in C++ way of classes comprising objects structure
> and methods in one entity.  In S4 they are completely separated.
>
>   > setMethod("ProcessTag", signature(tag = "RoccerUsage"), function(tag, roc) ...)
>   > setMethod("OutRd", signature(tag = "RoccerUsage"), function(tag) ...)
>
> Ah ... ok, reading it now :) Somehow I missed this on the first skim.
> Sure, this is the way!
>
>   > setClass("RoccerUsage", contains = "Roccer")
>   > setMethod("Process", signature(tag = "RoccerUsage"), function(tag,
>   > rocblocks) ...)
>   > setMethod("Output", signature(tag = "RoccerUsage"), function(tag,
>   > rocblocks) ...)
>
> But wait a second. Why do you need Roccers? What is roccer? You have tag
> classes representing the tags.

I'm thinking of a tag as the string "@mytag the tag's content". The
Roccer is the object that converts it into something useful - but I
think you're right that in this model that would be a Tag. The
distinction would be tag strings (parsed) and Tag objects (parsed and
processed).

Hadley

-- 
Assistant Professor
Department of Statistics / Rice University
http://had.co.nz/


More information about the Roxygen-devel mailing list