Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation for R6 methods #388

Closed
hadley opened this issue Oct 6, 2015 · 47 comments · Fixed by #922
Closed

Documentation for R6 methods #388

hadley opened this issue Oct 6, 2015 · 47 comments · Fixed by #922
Labels
feature a feature request or enhancement

Comments

@hadley
Copy link
Member

hadley commented Oct 6, 2015

Should look something like this:


Methods

Method 1

class$method(a, b, c = 12)

Description.

Parameters

  • Param 1
  • Param 2
  • Param 3

Returns

What the method returns

Example

a <- 10
b <- 5
a + b
#> [1]

Rd would look something like:

\section{Methods}{
\subsection{\code{method1}}{

\code{class$method1(a, b, c = 12}

\subsection{Parameter}{

}
}

and similarly for fields.

Parameters, returns, and methods would be optional. If no methods use them, just list usage + (optional) description with bullets. Can also use top-level @examples if you want to put all examples in one place.

@krlmlr
Copy link
Member

krlmlr commented Oct 11, 2015

How about also parsing roxygen comments inside the objects that are documented? They seem to be available in the parsed list refs list. Would be useful also for DBItest.

@krlmlr
Copy link
Member

krlmlr commented Oct 20, 2015

Why not one .Rd file per method? The list of methods could be autogenerated, with links to detailed methods documentation if available.

\section{Public methods}{
  \itemize{
    \item{\code{initialize(a, b, c = 2)}
    \item{\code{\link[=Class$method1]{method1}(d, e)}: Title of method documentation
  }
}

@hadley
Copy link
Member Author

hadley commented Oct 21, 2015

I don't think you want want file per method, because with this style of OO you tend to have lots and lots of little methods. Most docs for js, ruby, python, and java document all methods on a single page.

@Enchufa2
Copy link

Enchufa2 commented Nov 9, 2015

Hi! If I may make a couple of suggestions, it would be great something like this (I don't know if I'm defining too many subsections):

Class

Description

blablabla

Inherits

BaseClassLink

Usage

Class$new(param1, ...)

Arguments

  • param1
  • ...

Public attributes

  • attr1
  • ...

Public methods

your structure here

Active bindings

ActiveBinding1

class$activebinding1
class$activebinding1 <- value

Description.

Parameters

  • value

etc. (same structure as methods)

Details

details

References

refs

See also

blablabla

Examples

General examples

@levithatcher
Copy link

Sorry to bug, but is there anyone actively working on this? I love roxygen2 and R6 and would very much love and appreciate built-in documentation for my methods (and not just their class). Overall, thanks for the wonderful work you've already done!

@hhoeflin
Copy link

hhoeflin commented Aug 30, 2016

The docstrings from #465 allow also for \code, but you need to escape the , so use a \code. I agree this is a little annoying. I used it as it is similar to how Reference classes are implemented, and expanded from there. An alternative would be to have an R-comment right after the top of the function. When sourcing the code for processing, the comments could be extracted, so then #' could be used.

@epicfarmer
Copy link

@hhoeflin Right, that makes sense. I would say that a #' comment right after the start of the function would be better if possible.

I've edited the above to also document inherited methods recursively if they are not overwritten. I'm not sure exactly how to go about submitting a pull request. Should I do it directly here, or as a modification of @hhoeflin 's repository first?

Additionally, it seems that this code meets all of the ideas above except:

  • Documenting class$method rather than just method.
  • Documenting the inheriting class with a link.
  • Separating Methods from Active Bindings.

@hadley Do you think the documentation specified above is adequate, or are you hoping for more discussion about how R6 classes should be documented?

@hadley
Copy link
Member Author

hadley commented Aug 30, 2016

@epicfarmer what is "this code" that you are talking about?

Before you implement a pull request, I think you need to write up an overall design document that describes how documenting R6/RC classes would work, fleshing out the sketches described above.

@hhoeflin
Copy link

@epicfarmer Yes - it doesn't separate active methods from regular ones - but it does label active methods as such. There is certainly quite a few things to be improved from my original implementation. I intended it as a first start towards a discussion.

@epicfarmer
Copy link

epicfarmer commented Aug 30, 2016

@hadley: Sorry, by I edited the code from pull request in #465 . Do you have an example design document from a previous addition?

@hhoeflin: That makes sense.

Starting with methods as using the same documentation as functions makes sense. However, I think it might be good to additionally have optional fields @ references and @ mutates to document members of the class which are used and modified respectively.

@hadley
Copy link
Member Author

hadley commented Aug 30, 2016

I don't — but I'd recommend using google docs so that it's easier to comment/collaborate.

@epicfarmer
Copy link

https://drive.google.com/open?id=0BzaZpCcBss4Jd2twbDhNTUdMczA

Is something like this what you had in mind?

@hhoeflin
Copy link

@epicfarmer

Thanks for putting up the design document. Regarding the suggestion of a "mutate" keyword, could you describe its use more closely? Usually for me, accessor methods are defined in OOP in order to abstract away the internal structure of an object. To me, documenting how an accessor method changes internal fields (especially private ones as in your example) seems to be counter to that approach of hiding the internal implementation from the user.

@epicfarmer
Copy link

@hhoeflin

So, the example was written quickly, and so might not be adequate. I think that you're right in that documenting the internals of the class is not really what we want to do.

The original idea behind the mutate keyword, was that we needed a separate field from @param and @return to correspond to members of the object. @reference and @mutate are places in the object where input and output go respectively. I think that if a method modifies a public field or active binding, then that should be documented: So for example:

  private = list(
     #' Hidden inner workings
    .a = 0
  ),
  public = list(
    incrementA = function(){
      #' Function which modifies hidden inner workings
      #' @reference a (Not sure if this should be here or not...)
      #' @mutate a Increases a by 1
      self$a = self$a+1
    }
  ),
  active = list(
    a = function(value){
      #' public interface to hidden workings
      if(missing(value)){
        return private$a;
    },
   private$.a = value
)

@hhoeflin
Copy link

@epicfarmer

Thanks - yes certainly makes sense to document changes to public fields.

@hadley
Copy link
Member Author

hadley commented Sep 28, 2016

I think that is out-of-scope for now. It would be better to get something minimal working before adding rarely needed features.

@epicfarmer
Copy link

@hadley Getting something minimal working should definitely come first. If you get a chance, could you glance at the document and make sure its what you had in mind.

@hhoeflin Since you already made most of the changes to the code, could you modify the document to include an overview of how you did what you already did? I'm not familiar enough with the structure of roxygen to do it.

I've added all the roxygen keywords that seem remotely relevant. I think most of them are out of scope for right now, but I've marked a few that aren't.

@hadley
Copy link
Member Author

hadley commented Sep 28, 2016

It would be easier to read/comment if it was a properly styled shared google document, not an Rmd uploaded to google drive.

@epicfarmer
Copy link

epicfarmer commented Sep 28, 2016

I forgot that the markdown interface isn't normally part of google documents.

https://docs.google.com/document/d/1y8jNDX6tcdWXP4yXNHvwtygYrqoZqSMcHkPheh2pMGY/edit?usp=sharing

I'll remove the other one then, so there isn't any confusion.

@epicfarmer
Copy link

As far as documenting methods, perhaps something like this for the Rd:

https://docs.google.com/document/d/1axFKclZ5Zf5eX2hnLpLZA0sQh7OACMmmXpP2YT7KJo0/edit?usp=sharing

To clarify, for R6Class documentation to be implemented which of the following (or perhaps other things) are needed:

how to go from commented R code to Rd documents
designing the desired Rd documents (this is what the google doc addresses)
Dealing with R6 specific structures (inheritance, active bindings, private methods/members)

@hadley
Copy link
Member Author

hadley commented Feb 27, 2017

I was thinking more like:

\itemize{\code(method1(arg1, arg2, ...))}{
  Descriptive stuff and such
  \methodtable{Arguments}{
    \methoditem{arg1}{...}
    \methoditem{arg2}{...}
    \methoditem{\dots}{...}
  }
  
  \bold{Return value}: something returned
}

@jkamins7
Copy link

jkamins7 commented Mar 15, 2017

The reason I was thinking of allowing more than one return was so that people could document behaviour where methods store results as members of the class. For example:

R6Class(
    public = list(
        member1 = 1,
        member2 = 2,
        method = function(x,y){
            #' @return self$member1 ...
            #' @return self$member2 ...
            self$member1 = x
            self$member2 = y
        }
    )
)

With R6 classes, its important to be able to document this sort of behaviour somehow, because its important for users to know where the results are stored in the event that they aren't returned. It would make sense to give it its own section, or to put it with returns, but I think not documenting it at all would be a mistake.

(This is the same user as @epicfarmer . I seem to be signed in on another account.)

@hadley
Copy link
Member Author

hadley commented Mar 15, 2017

Abusing return to document in-place mutation seems like a bad idea to me.

@epicfarmer
Copy link

The gist is that:
Its hard to tell different methods apart at a glance. Also, too many methods clogs up the screen quickly (its even worse in Rstudio). This is with subsection, but the itemize version makes it even harder to tell methods apart easily. It might be better to change the Arguments and Return Value pieces to italics instead of bold?

generic
matrixmetadata1
matrixmetadata2
metadatacontainer

@epicfarmer
Copy link

I've made some changes to the document

https://docs.google.com/document/d/1axFKclZ5Zf5eX2hnLpLZA0sQh7OACMmmXpP2YT7KJo0/edit?usp=sharing

I wanted to avoid using horizontal spacing, but I think its unavoidable. It seems much easier to read the output this way.

generic
matrixmetadata-1
matrixmetadata-2
matrixmetadata-3

Does this seem ok to you?

@hadley
Copy link
Member Author

hadley commented Mar 21, 2017

I think using non-semantic markup in that way is a bad idea. It would be better to keep thinking how we can using existing markup without too many additional hacks.

@epicfarmer
Copy link

By non-semantic markup, are you referring to the switch from subsection to bold with carriage returns, or the use of verb for a tab, or both?

@hadley
Copy link
Member Author

hadley commented Mar 22, 2017

Both

@epicfarmer
Copy link

epicfarmer commented Mar 22, 2017 via email

@jkamins7
Copy link

I've updated the document: https://docs.google.com/document/d/1axFKclZ5Zf5eX2hnLpLZA0sQh7OACMmmXpP2YT7KJo0/edit?usp=sharing

This acheives a similar effect to the previous pictures, but is less of a hack. @hadley , are there any other parts that feel hacked, which I should try to remove?

@manumart
Copy link

Hi everybody,
as others, I love using R6 classes but am wondering how I should/could get them documented (especially methods). Are there any guidelines around so far about how this is to be done?

Cheers, Manuel

@hadley
Copy link
Member Author

hadley commented Oct 1, 2017

More detailed sketch with @wch:

#' My awesome class
#' 
#' @export
#' @examples 
#' # Integrative examples go here.
MyClass <- R6::R6Class("MyClass", public = list(
  #' @section Important things:
  #' These are important. More text here. 
  #' Followed by list of methods.
  NULL,
  
  #' Get a single value. Modifies in place.
  #' @param key name of key to retrieve
  get = function(key) {},
  
  #' Set a single value.
  #' @param key name of key to set
  #' @param value new value
  #' @return old value
  set = function(key, val) {}
))
\section{Methods and fields}{

\subsection{Whatever you want}{

Some text here. Followed by list of methods.

\describe{
  \item{\code{$get(key)}}{Gets a single value}

  \item{\code{$set(key, value)}}{

  Gets a single value

  \tabular{ll}{
  \code{key} \tab name of key \cr
  \code{value} \tab new value \cr
  }
  }

  \item{\code{$set(key, value)}}{

  Gets a single value

  \itemize{
  \item \code{key}: name of key
  \item \code{value}: new value
  }
  }

  \item{\code{$set(key, value)}}{

  Gets a single value

  \describe{
  \item{\code{key}}{name of key}
  \item{\code{value}}{new value. This is a long sentence which is going to
    span multiple lines.This is a long sentence which is going to
    span multiple lines.}
  }
  }

  \item{\code{$set(key, value)}}{

  Gets a single value

  — \code{key}: name of key

  — \code{value}: new value. This is a long sentence which is going to
    span multiple lines.This is a long sentence which is going to
    span multiple lines.
  }

  \item{\code{$set(key, value)}}{

  Gets a single value

  \code{key} — name of key

  \code{value} — new value. This is a long sentence which is going to
    span multiple lines. This is a long sentence which is going to
    span multiple lines.

  Returns: self, invisibly.

  }



}
}
}

White list of tags specifically for methods

  • description
  • @param
  • @return

Fields & active bindings just get a description.

In class documentation, automatically link to docs for parent class.

@section adds structure. Title + optional text.

Out of scope for now:

  • examples - in class
  • R6 inheritance - how to parse Rd?
  • internal docs for private methods/fields
  • methods via $set

Also need to create objects

List of 6
 $ title      : chr "accumulate_n"
 $ signature  : chr "accumulate_n(inputs, shape = NULL, tensor_dtype = NULL, name = NULL)"
 $ returns    : chr "A `Tensor` of same shape and type as the elements of `inputs`."
 $ description: chr "Returns the element-wise sum of a list of tensors."
 $ details    : chr "Optionally, pass `shape` and `tensor_dtype` for shape and type checking,\notherwise, these are inferred. NOTE: This operation i"| __truncated__
 $ sections   :List of 2
  ..$ NOTE: This operation is not differentiable and cannot be used if inputs depend: chr "on trainable variables. Please use `tf.add_n` for such cases."
  ..$ Raises                                                                        : chr "ValueError: If `inputs` don't all have same shape and dtype or the shape cannot be inferred."

One page per class.

One section for all fields/methods/active binding,
optionally separated into subsection.

@hhoeflin
Copy link

hhoeflin commented Oct 2, 2017

Is there a way to also cover the $set method? I make very heavy use of it to emulate multiple inheritance. So far i have used a method similar to the one implemented for rcclasses using strings and this also works with "set" (see https://github.com/hhoeflin/roxygen).

For an example where it is used, see https://github.com/hhoeflin/hdf5r

and the pkgdown doc on

https://hhoeflin.github.com/hdf5r

One option maybe to also support strings at the beginning of the function instead of or in addition to the comments before the function definition that you propose above?

@jkamins7
Copy link

jkamins7 commented Oct 2, 2017

@hadley I'm a bit confused by your example in the first part. It seems like you documented the two methods in a few different ways. Are you asking for feedback? (If you are, I think i prefer the nested \describe )

As far as the scoping goes, this seems like a fine place to start. Do you still need more complete examples before moving on? Also, in general, how can I help move this forward?

@hadley
Copy link
Member Author

hadley commented Oct 2, 2017

I'm not asking for feedback, I'm just dumping my notes. Unfortunately I don't think there's any way for someone else to push this forward - I want to have a solid mental model of the problem before implementing anything.

@jeffwong-nflx

This comment has been minimized.

@jkamins7
Copy link

As an update, the package I was originally wanting this for is released on cran as ForecastFramework. I have a workaround for documentation that uses roxygen-like syntax, and parses to Rd. I would like to be able to convert that to roxygen if/when this is implemented. It would be helpful if you could keep us updated on your thoughts about which tags will mean what, even if they are currently not implemented. I've been using this post as a guide more or less.

@gaborcsardi
Copy link
Member

Hi all, I opened a PR for R6 support: #922. It is a draft, and some things are still coming, but if you give it a try, then feedback is appreciated. Thanks!

@yoonsucho
Copy link

Hi all,
I use R6 class for my package and have a few functions which were added by using $set method. I wonder if there is any updates on the documentation for the $set method? It would be much appreciated if you could help me find a solution for this.
Looking forward to hearing from you. Thanks!

@gaborcsardi
Copy link
Member

See #931.

@kainhofer
Copy link

Thanks for implementing R6 support in roxygen2. Works quite well, except for one feature I have not yet been able to do: How do I link to one particular methods of an R6 class?

#' Test R6 class
#' @export
TestR6 = R6Class(
    "TestR6",
    public = list(
        #' @description Example method
        myMethodA = function() {1:10}
    )
);

#' Test function
#' 
#' This function is a wrapper for [TestR6$myMethodA()] or [TestR6#method-myMethodA]
#' or [TestR6#method-myMethodA()], but neither of these links work....
#' @export
myMethodB = function(r) { r$myMethodA() }

Neither of the links in the documentation of myMethodB appear to correctly link to the TestR6$myMethodA() method, just to the general TestR6 class.... For classes with potentially dozens of methods, this is not very user-friendly.

What is the correct way to link to one particular method?

@hongyuanjia
Copy link

Hi @kainhofer, I have the same question before. Please see #955. Basically, it is only possible in HTML output.

@gaborcsardi
Copy link
Member

@kainhofer Can you please open a separate issue? We'll be working on R6 again soon, and since I have seen R-core and packages relying on the structure of the help files, we can consider adding proper support for in-page links.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.