GithubHelp home page GithubHelp logo

sloop's Introduction

sloop sloop website

CRAN status R-CMD-check Codecov test coverage

The goal of sloop is to provide tools to help you interactively explore and understand object oriented programming in R, particularly with S3.

Please note that unlike other r-lib packages, sloop only works with R 3.3 and later.

Installation

You can install sloop from github with:

# install.packages("pak")
pak::pak("r-lib/sloop")

Usage

library(sloop)

sloop provides a variety of tools for understanding how S3 works. The most useful is probably s3_dispatch(). Given a function call, it shows the set of methods that are considered, found, and actually called:

s3_dispatch(print(Sys.time()))
#> => print.POSIXct
#>    print.POSIXt
#>  * print.default

To the best of my ability it covers all the details of S3 method dispatch including group generics, internal generics, implicit classes, and use of NextMethod() (indicated by ->):

# Implicit class
x <- matrix(1:6, nrow = 2)
s3_dispatch(print(x))
#>    print.matrix
#>    print.integer
#>    print.numeric
#> => print.default

# Internal generic 
length.numeric <- function(x) 10
s3_dispatch(length(x))
#>    length.matrix
#>    length.integer
#>  * length.numeric
#>    length.default
#> => length (internal)

s3_dispatch(length(structure(x, class = "numeric")))
#> => length.numeric
#>    length.default
#>  * length (internal)

# NextMethod
s3_dispatch(Sys.Date()[1])
#> => [.Date
#>    [.default
#> -> [ (internal)

# group generic + NextMethod()
s3_dispatch(sum(Sys.Date()))
#>    sum.Date
#>    sum.default
#> => Summary.Date
#>    Summary.default
#> -> sum (internal)

It also provides tools for determing what type of function or object you’re dealing with:

ftype(t)
#> [1] "S3"      "generic"
ftype(t.test)
#> [1] "S3"      "generic"
ftype(t.data.frame)
#> [1] "S3"     "method"

otype(1:10)
#> [1] "base"
otype(mtcars)
#> [1] "S3"
otype(R6::R6Class()$new())
#> [1] "R6"

And for retrieving the methods associated with a generic or class:

s3_methods_class("factor")
#> # A tibble: 28 × 4
#>    generic       class  visible source
#>    <chr>         <chr>  <lgl>   <chr> 
#>  1 [             factor TRUE    base  
#>  2 [[            factor TRUE    base  
#>  3 [[<-          factor TRUE    base  
#>  4 [<-           factor TRUE    base  
#>  5 all.equal     factor TRUE    base  
#>  6 as.character  factor TRUE    base  
#>  7 as.data.frame factor TRUE    base  
#>  8 as.Date       factor TRUE    base  
#>  9 as.list       factor TRUE    base  
#> 10 as.logical    factor TRUE    base  
#> # ℹ 18 more rows

s3_methods_generic("summary")
#> # A tibble: 39 × 4
#>    generic class                 visible source             
#>    <chr>   <chr>                 <lgl>   <chr>              
#>  1 summary aov                   TRUE    stats              
#>  2 summary aovlist               FALSE   registered S3method
#>  3 summary aspell                FALSE   registered S3method
#>  4 summary check_packages_in_dir FALSE   registered S3method
#>  5 summary connection            TRUE    base               
#>  6 summary data.frame            TRUE    base               
#>  7 summary Date                  TRUE    base               
#>  8 summary default               TRUE    base               
#>  9 summary ecdf                  FALSE   registered S3method
#> 10 summary factor                TRUE    base               
#> # ℹ 29 more rows

sloop's People

Contributors

hadley avatar jimhester avatar krlmlr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

sloop's Issues

Document notation in `s3_dispatch()`

For example,

library(sloop)
x <- 1:5
y <- factor(letters[1:5])

sloop::s3_dispatch(summary(x))
#>    summary.integer
#>    summary.numeric
#> => summary.default
sloop::s3_dispatch(summary(y))
#> => summary.factor
#>  * summary.default

What does => indicate? *, ->? In the console, some functions appear greyed out, what does that mean?

This seems to be done partially in the README but it needs to make it into the docs.

Upkeep for sloop (2019)

2020

  • usethis::use_package_doc()
    Consider letting usethis manage your @importFrom directives here.
    usethis::use_import_from() is handy for this.
  • usethis::use_testthat(3) and upgrade to 3e, testthat 3e vignette
  • Align the names of R/ files and test/ files for workflow happiness.
    The docs for usethis::use_r() include a helpful script.
    usethis::rename_files() may be be useful.

2021

  • usethis::use_tidy_description()
  • usethis::use_tidy_dependencies()
  • usethis::use_tidy_github_actions() and update artisanal actions to use setup-r-dependencies
  • Remove check environments section from cran-comments.md
  • Bump required R version in DESCRIPTION to 3.6
  • Use lifecycle instead of artisanal deprecation messages, as described in Communicate lifecycle changes in your functions
  • Make sure RStudio appears in Authors@R of DESCRIPTION like so, if appropriate:
    person("RStudio", role = c("cph", "fnd"))

2022

2023

Necessary:

  • Update email addresses *@rstudio.com -> *@posit.co
  • Update copyright holder in DESCRIPTION: person(given = "Posit Software, PBC", role = c("cph", "fnd"))
  • Run devtools::document() to re-generate package-level help topic with DESCRIPTION changes
  • Update logo (https://github.com/rstudio/hex-stickers); run use_tidy_logo()
  • usethis::use_tidy_coc()
  • usethis::use_tidy_github_actions()

Optional:

  • Review 2022 checklist to see if you completed the pkgdown updates
  • Prefer pak::pak("org/pkg") over devtools::install_github("org/pkg") in README
  • Consider running use_tidy_dependencies() and/or replace compat files with use_standalone()
  • use_standalone("r-lib/rlang", "types-check") instead of home grown argument checkers
  • Add alt-text to pictures, plots, etc; see https://posit.co/blog/knitr-fig-alt/ for examples

Created on 2023-10-30 with usethis::use_tidy_upkeep_issue(), using usethis v2.2.2.9000

error thrown when no methods exist

If there are no existing methods, s4_methods_class, as a wrapper of utils::methods,
is not expected to throw an error:

setClass("virtualA", contains = "VIRTUAL")
s4_methods_class("virtualA")
#> Error in `$<-.data.frame`(`*tmp*`, class, value = "virtualA") : 
# replacement has 1 row, data has 0

However,

attr(utils::methods(class = "virtualA"), "info")
#> [1] visible from    generic isS4   
# <0 rows> (or 0-length row.names)

Maybe sloop:::methods_class can do a check for row number or other way to avoid the error...

Update installation URL in README

I know Github handles redirects automatically, but it might confuse people to point them to devtools::install_github("hadley/S3"). Should be updated to devtools::install_github("hadley/sloop")

Sloop rebranding

Now that vctrs exists and fully fleshes out what you need to create S3 vectors, I think it's more clear that sloop should be a package aimed at interactive exploration, including show case of what implement of base R vectors might look like, if we rewrote them from scratch.

To this end, will remove reconstruct() (now vctrs::vec_recast()), and the new_s3_*() family of functions (since they're only thin wrappers, and if you need them yourself you might as well rebind).

Bug s3_dispatch() for cbind()

The generic should dispatch in cbind.default, why didn't it happen? The output in s3_dispatch() is correct, as I would expect. But that didn't happen when I use the generic. See the code below.

Code:
cbind.default <- function(x, ...) "Test!"
x <- 1:10
sloop::s3_dispatch(cbind(structure(x, class = "numeric")))
cbind(structure(x, class = "numeric"))

Example (Output):

cbind.default <- function(x, ...) "Test!"
x <- 1:10
sloop::s3_dispatch(cbind(structure(x, class = "numeric")))
cbind.numeric
=> cbind.default

  • cbind (internal)

cbind(structure(x, class = "numeric"))
[,1]
[1,] 1
[2,] 2
[3,] 3
[4,] 4
[5,] 5
[6,] 6
[7,] 7
[8,] 8
[9,] 9
[10,] 10

List all methods

Something like:

library(tidyverse)

methods("format") %>% 
  attr(., "info") %>% 
  as_tibble() %>% 
  rownames_to_column("method") %>% 
  mutate(class = stringr::str_split_fixed(method, "\\.", 2)[, 2]) %>% 
  select(generic, class, visible, from)

May want to consider implementing methods() from scratch — will help understand precise semantics of method lookup.

Release sloop 1.0.0

Prepare for release:

  • devtools::check_win_devel()
  • rhub::check_for_cran()

Perform release:

  • Bump version (in DESCRIPTION and NEWS)
  • devtools::check_win_devel() (again!)
  • devtools::submit_cran()
  • pkgdown::build_site()
  • Approve email

Wait for CRAN...

  • Tag release
  • Bump dev version

Template from r-lib/usethis#338

Release sloop 1.0.1

Prepare for release:

  • devtools::check()
  • devtools::check_win_devel()
  • rhub::check_for_cran()
  • revdepcheck::revdep_check(num_workers = 4)
  • Polish NEWS
  • Polish pkgdown reference index

Submit to CRAN:

  • usethis::use_version()
  • Update cran-comments.md
  • devtools::submit_cran()
  • Approve email

Wait for CRAN...

  • Accepted 🎉
  • usethis::use_github_release()
  • usethis::use_dev_version()
  • Tweet

Move `master` branch to `main`

The master branch of this repository will soon be renamed to main, as part of a coordinated change across several GitHub organizations (including, but not limited to: tidyverse, r-lib, tidymodels, and sol-eng). We anticipate this will happen by the end of September 2021.

That will be preceded by a release of the usethis package, which will gain some functionality around detecting and adapting to a renamed default branch. There will also be a blog post at the time of this master --> main change.

The purpose of this issue is to:

  • Help us firm up the list of targetted repositories
  • Make sure all maintainers are aware of what's coming
  • Give us an issue to close when the job is done
  • Give us a place to put advice for collaborators re: how to adapt

message id: euphoric_snowdog

Bug in s3_dispatch

(Probably due to a change in R 4.0; I discovered this when solving the regarding exercise in Adv R)

# Output in R 4.0.3
x <- structure(1:10, class = "test")

sloop::s3_dispatch(t(x))
#> => t.test
#>  * t.default
t(x)
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,]    1    2    3    4    5    6    7    8    9    10
#> attr(,"class")
#> [1] "test"
t.test(x)
#> 
#>  One Sample t-test
#> 
#> data:  x
#> t = 5.7446, df = 9, p-value = 0.0002782
#> alternative hypothesis: true mean is not equal to 0
#> 95 percent confidence interval:
#>  3.334149 7.665851
#> sample estimates:
#> mean of x 
#>       5.5

Created on 2020-12-31 by the reprex package (v0.3.0)

Inconsistency when referring to a function programmatically

If I refer to a function via a variable, the class column of s3_methods_generic isn't what I'd expect.

this_func <- mean
sloop::s3_methods_generic("this_func")
#> Warning in .S3methods(generic.function, class, envir): generic function
#> 'this_func' dispatches methods for generic 'mean'
#> # A tibble: 6 x 4
#>   generic class         visible source                      
#>   <chr>   <chr>         <lgl>   <chr>                       
#> 1 mean    mean.Date     TRUE    base                        
#> 2 mean    mean.default  TRUE    base                        
#> 3 mean    mean.difftime TRUE    base                        
#> 4 mean    mean.POSIXct  TRUE    base                        
#> 5 mean    mean.POSIXlt  TRUE    base                        
#> 6 mean    mean.quosure  FALSE   registered S3method for mean
sloop::s3_methods_generic("mean")
#> # A tibble: 7 x 4
#>   generic class      visible source             
#>   <chr>   <chr>      <lgl>   <chr>              
#> 1 mean    Date       TRUE    base               
#> 2 mean    default    TRUE    base               
#> 3 mean    difftime   TRUE    base               
#> 4 mean    POSIXct    TRUE    base               
#> 5 mean    POSIXlt    TRUE    base               
#> 6 mean    quosure    FALSE   registered S3method
#> 7 mean    vctrs_vctr FALSE   registered S3method

We already have the "generic" column from utils::method, should it use that for generating the "class" column (rather than x)?

I can PR if there isn't a reason to use the generic_esc that I'm not thinking of; using info from methods seems safer.

Behavior of s3_get_method with all.equal

Is the fact that the second line generates an error a bug or a feature? I'm not sure how to interpret this...

ftype(all.equal)
s3_get_method(all.equal(1, 1))

s3_dispatch error when using namespace scoping

sloop::s3_dispatch(vctrs::vec_arith("+", 2, 3))
#> Error: Mapped vectors must have consistent lengths:
#> * `.x` has length 3
#> * `.y` has length 2

Not sure if it is better to handle this case specially or to modify the check

 if (!is_call(call)) {
        stop("`call` must be a function call", call. = FALSE)
    }

sloop::reconstruct() + sticky::sticky() = heart?

Jumping off of hadley/adv-r#1148, attribute retention seems like an important thing.

sloop::reconstruct() is a great helper in that regard.

However, it's hard to foresee for users (or, more likely, package developers) for which generics (other than, say, dplyr::mutate() some mutate.myClass() would have to be built.
You mention in adv-r that e.g. dplyr will do this out of the box at some point (which is fantastic).
However, this is unlikely for base functions such as subset() and friends.

It also seems a bit of a duplicate effort to build mutate.myClassA() for pkgA and have someone else build mutate.myClassB() for pkgB when essentially, all mutate.xClass() do the same thing.

I was wondering whether it might therefore make sense to combine sloop::reconstruct with sticky::sticky (or factor this out to a separate package):

  1. Offer a separate class (say, sticky) for objects which are supposed to retain their attributes.
    @ctbrown's / @decisionpatterns' sticky already does this, though with some limitations.
  2. Implement, say, subset.sticky() methods the frequently used (base) functions which are unlikely to get such methods from their developers (which would be preferable).
  3. Implement a reconstruct.default() which pkg developers can fall back on when developing their own some_function.myClass().

I obviously have no business suggesting this and too little understanding of R plumbing for this, but I noticed these two related efforts and thought I'd write this up here.

Would also be happy to chip in, but need some direction.

Quoted parens does not inherit from call

p. <- quote((.))
sloop::s3_class(p.)
#> [1] "("    "call"

foo <- function(x) UseMethod("foo")
foo.call <- function(x) "produced inside foo.call"

foo(p.)
#> Error in UseMethod("foo"): no applicable method for 'foo' applied to an object of class "("
`foo.(` <- function(x) "produced inside foo.("
foo(p.)
#> [1] "produced inside foo.("

Created on 2019-11-22 by the reprex package (v0.3.0)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.