├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ ├── rchk.yml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── S3.R ├── S4.R ├── S7-package.R ├── aaa.R ├── base.R ├── class-spec.R ├── class.R ├── compatibility.R ├── constructor.R ├── convert.R ├── data.R ├── external-generic.R ├── generic-spec.R ├── generic.R ├── inherits.R ├── method-dispatch.R ├── method-introspect.R ├── method-ops.R ├── method-register.R ├── property.R ├── special.R ├── super.R ├── union.R ├── utils.R ├── valid.R └── zzz.R ├── README.Rmd ├── README.md ├── S7.Rproj ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── man ├── S4_register.Rd ├── S7_class.Rd ├── S7_data.Rd ├── S7_inherits.Rd ├── S7_object.Rd ├── as_class.Rd ├── base_classes.Rd ├── base_s3_classes.Rd ├── class_any.Rd ├── class_missing.Rd ├── convert.Rd ├── method-set.Rd ├── method.Rd ├── method_explain.Rd ├── methods_register.Rd ├── new_S3_class.Rd ├── new_class.Rd ├── new_external_generic.Rd ├── new_generic.Rd ├── new_property.Rd ├── new_union.Rd ├── prop.Rd ├── prop_names.Rd ├── props.Rd ├── super.Rd └── validate.Rd ├── revdep ├── README.md ├── cloud.noindex │ └── b283e3a4-953b-4875-b3dc-ee76fe345f59 │ │ ├── crumble.tar.gz │ │ ├── crumble │ │ ├── DESCRIPTION │ │ ├── new │ │ │ └── crumble.Rcheck │ │ │ │ ├── crumble-Ex.Rout │ │ │ │ └── tests │ │ │ │ └── testthat.Rout │ │ └── old │ │ │ └── crumble.Rcheck │ │ │ ├── crumble-Ex.Rout │ │ │ └── tests │ │ │ └── testthat.Rout │ │ ├── fr.tar.gz │ │ ├── fr │ │ ├── DESCRIPTION │ │ ├── new │ │ │ └── fr.Rcheck │ │ │ │ ├── fr-Ex.Rout │ │ │ │ └── tests │ │ │ │ └── testthat.Rout.fail │ │ └── old │ │ │ └── fr.Rcheck │ │ │ ├── fr-Ex.Rout │ │ │ └── tests │ │ │ └── testthat.Rout │ │ ├── monad.tar.gz │ │ ├── monad │ │ ├── DESCRIPTION │ │ ├── new │ │ │ └── monad.Rcheck │ │ │ │ ├── monad-Ex.Rout │ │ │ │ └── tests │ │ │ │ └── testthat.Rout │ │ └── old │ │ │ └── monad.Rcheck │ │ │ ├── monad-Ex.Rout │ │ │ └── tests │ │ │ └── testthat.Rout │ │ ├── waldo.tar.gz │ │ └── waldo │ │ ├── DESCRIPTION │ │ ├── new │ │ └── waldo.Rcheck │ │ │ ├── tests │ │ │ └── testthat.Rout │ │ │ └── waldo-Ex.Rout │ │ └── old │ │ └── waldo.Rcheck │ │ ├── tests │ │ └── testthat.Rout │ │ └── waldo-Ex.Rout ├── cran.md ├── failures.md └── problems.md ├── src ├── .gitignore ├── init.c ├── method-dispatch.c └── prop.c ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── R-lt-4-3 │ │ └── method-dispatch.md │ ├── S3.md │ ├── S4.md │ ├── base.md │ ├── class-spec.md │ ├── class.md │ ├── constructor.md │ ├── convert.md │ ├── external-generic.md │ ├── generic-spec.md │ ├── generic.md │ ├── inherits.md │ ├── method-dispatch.md │ ├── method-introspect.md │ ├── method-register.md │ ├── property.md │ ├── special.md │ ├── super.md │ ├── union.md │ ├── valid.md │ └── zzz.md │ ├── helper.R │ ├── t0 │ ├── DESCRIPTION │ ├── NAMESPACE │ └── R │ │ └── t0.R │ ├── t1 │ ├── DESCRIPTION │ ├── NAMESPACE │ └── R │ │ └── t1.R │ ├── t2 │ ├── DESCRIPTION │ ├── NAMESPACE │ └── R │ │ └── t2.R │ ├── test-S3.R │ ├── test-S4.R │ ├── test-base-r.R │ ├── test-base.R │ ├── test-class-spec.R │ ├── test-class.R │ ├── test-constructor.R │ ├── test-convert.R │ ├── test-data.R │ ├── test-external-generic.R │ ├── test-generic-spec.R │ ├── test-generic.R │ ├── test-inherits.R │ ├── test-method-dispatch.R │ ├── test-method-introspect.R │ ├── test-method-ops.R │ ├── test-method-register.R │ ├── test-property.R │ ├── test-special.R │ ├── test-super.R │ ├── test-union.R │ ├── test-valid.R │ └── test-zzz.R └── vignettes ├── .gitignore ├── S7.Rmd ├── classes-objects.Rmd ├── compatibility.Rmd ├── generics-methods.Rmd ├── minutes ├── 2021-05-18.Rmd ├── 2022-02-12.Rmd ├── 2022-03-08.Rmd ├── 2022-08-01.Rmd └── 2022-09-12.Rmd ├── motivation.Rmd ├── packages.Rmd ├── performance.Rmd └── spec ├── .gitignore ├── design.Rmd ├── dispatch.Rmd ├── proposal.Rmd └── requirements.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^\.github$ 5 | ^spec$ 6 | ^proposal$ 7 | ^minutes$ 8 | ^codecov\.yml$ 9 | ^README\.Rmd$ 10 | ^script\.R$ 11 | ^README_cache$ 12 | ^_pkgdown\.yml$ 13 | ^vignettes/[^/]+_cache$ 14 | ^docs$ 15 | ^pkgdown$ 16 | ^cran-comments\.md$ 17 | ^CRAN-SUBMISSION$ 18 | ^compile_commands\.json$ 19 | ^\.cache$ 20 | ^\.vscode$ 21 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | workflow_dispatch: 9 | push: 10 | branches: [main, master] 11 | pull_request: 12 | branches: [main, master] 13 | 14 | name: R-CMD-check 15 | 16 | permissions: read-all 17 | 18 | jobs: 19 | R-CMD-check: 20 | runs-on: ${{ matrix.config.os }} 21 | 22 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 23 | 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | config: 28 | - {os: macos-latest, r: 'release'} 29 | 30 | - {os: windows-latest, r: 'release'} 31 | # use 4.1 to check with rtools40's older compiler 32 | - {os: windows-latest, r: '4.1'} 33 | 34 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 35 | - {os: ubuntu-latest, r: 'release'} 36 | - {os: ubuntu-latest, r: 'oldrel-1'} 37 | - {os: ubuntu-latest, r: 'oldrel-2'} 38 | - {os: ubuntu-latest, r: 'oldrel-3'} 39 | - {os: ubuntu-latest, r: 'oldrel-4'} 40 | 41 | env: 42 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 43 | R_KEEP_PKG_SOURCE: yes 44 | 45 | steps: 46 | - uses: actions/checkout@v4 47 | 48 | - uses: r-lib/actions/setup-pandoc@v2 49 | 50 | - uses: r-lib/actions/setup-r@v2 51 | with: 52 | r-version: ${{ matrix.config.r }} 53 | http-user-agent: ${{ matrix.config.http-user-agent }} 54 | use-public-rspm: true 55 | 56 | - uses: r-lib/actions/setup-r-dependencies@v2 57 | with: 58 | extra-packages: any::rcmdcheck 59 | needs: check 60 | 61 | - uses: r-lib/actions/check-r-package@v2 62 | with: 63 | upload-snapshots: true 64 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 65 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - name: Build site 41 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 42 | shell: Rscript {0} 43 | 44 | - name: Deploy to GitHub pages 🚀 45 | if: github.event_name != 'pull_request' 46 | uses: JamesIves/github-pages-deploy-action@v4.5.0 47 | with: 48 | clean: false 49 | branch: gh-pages 50 | folder: docs 51 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: r-lib/actions/pr-fetch@v2 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - uses: r-lib/actions/setup-r@v2 26 | with: 27 | use-public-rspm: true 28 | 29 | - uses: r-lib/actions/setup-r-dependencies@v2 30 | with: 31 | extra-packages: any::roxygen2 32 | needs: pr-document 33 | 34 | - name: Document 35 | run: roxygen2::roxygenise() 36 | shell: Rscript {0} 37 | 38 | - name: commit 39 | run: | 40 | git config --local user.name "$GITHUB_ACTOR" 41 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 42 | git add man/\* NAMESPACE 43 | git commit -m 'Document' 44 | 45 | - uses: r-lib/actions/pr-push@v2 46 | with: 47 | repo-token: ${{ secrets.GITHUB_TOKEN }} 48 | 49 | style: 50 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 51 | name: style 52 | runs-on: ubuntu-latest 53 | env: 54 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 55 | steps: 56 | - uses: actions/checkout@v4 57 | 58 | - uses: r-lib/actions/pr-fetch@v2 59 | with: 60 | repo-token: ${{ secrets.GITHUB_TOKEN }} 61 | 62 | - uses: r-lib/actions/setup-r@v2 63 | 64 | - name: Install dependencies 65 | run: install.packages("styler") 66 | shell: Rscript {0} 67 | 68 | - name: Style 69 | run: styler::style_pkg() 70 | shell: Rscript {0} 71 | 72 | - name: commit 73 | run: | 74 | git config --local user.name "$GITHUB_ACTOR" 75 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 76 | git add \*.R 77 | git commit -m 'Style' 78 | 79 | - uses: r-lib/actions/pr-push@v2 80 | with: 81 | repo-token: ${{ secrets.GITHUB_TOKEN }} 82 | -------------------------------------------------------------------------------- /.github/workflows/rchk.yml: -------------------------------------------------------------------------------- 1 | name: rchk 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | 7 | jobs: 8 | rchk: 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - uses: actions/checkout@v4 13 | 14 | - uses: r-lib/actions/setup-pandoc@v2 15 | - uses: r-lib/actions/setup-r@v2 16 | - uses: r-lib/actions/setup-r-dependencies@v2 17 | 18 | - run: R CMD build . 19 | 20 | - run: docker pull kalibera/rchk:latest 21 | 22 | - name: run rchk 23 | run: | 24 | pkgtar=$(ls S7_*.tar.gz) 25 | mkdir -p rchk/packages 26 | mv $pkgtar rchk/packages/ 27 | cd rchk 28 | docker run -v `pwd`/packages:/rchk/packages kalibera/rchk:latest /rchk/packages/$pkgtar > rchk.log 2>&1 29 | cat rchk.log 30 | 31 | - run: cat rchk.log 32 | working-directory: rchk 33 | 34 | - name: upload rchk log 35 | uses: actions/upload-artifact@v4 36 | with: 37 | name: rchk-log 38 | path: rchk/rchk.log 39 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | cov <- covr::package_coverage( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 37 | ) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v4 42 | with: 43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 44 | file: ./cobertura.xml 45 | plugin: noop 46 | disable_search: true 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | 49 | - name: Show testthat output 50 | if: always() 51 | run: | 52 | ## -------------------------------------------------------------------- 53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 54 | shell: bash 55 | 56 | - name: Upload test results 57 | if: failure() 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: coverage-test-failures 61 | path: ${{ runner.temp }}/package 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | script.R 3 | inst/doc 4 | docs 5 | .Rhistory 6 | compile_commands.json 7 | .cache 8 | .vscode 9 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: S7 2 | Title: An Object Oriented System Meant to Become a Successor to S3 and S4 3 | Version: 0.2.0.9000 4 | Authors@R: c( 5 | person("Object-Oriented Programming Working Group", role = "cph"), 6 | person("Davis", "Vaughan", role = "aut"), 7 | person("Jim", "Hester", role = "aut", 8 | comment = c(ORCID = "0000-0002-2739-7082")), 9 | person("Tomasz", "Kalinowski", role = "aut"), 10 | person("Will", "Landau", role = "aut"), 11 | person("Michael", "Lawrence", role = "aut"), 12 | person("Martin", "Maechler", role = "aut", 13 | comment = c(ORCID = "0000-0002-8685-9910")), 14 | person("Luke", "Tierney", role = "aut"), 15 | person("Hadley", "Wickham", , "hadley@posit.co", role = c("aut", "cre"), 16 | comment = c(ORCID = "0000-0003-4757-117X")) 17 | ) 18 | Description: A new object oriented programming system designed to be a 19 | successor to S3 and S4. It includes formal class, generic, and method 20 | specification, and a limited form of multiple dispatch. It has been 21 | designed and implemented collaboratively by the R Consortium 22 | Object-Oriented Programming Working Group, which includes 23 | representatives from R-Core, 'Bioconductor', 'Posit'/'tidyverse', and 24 | the wider R community. 25 | License: MIT + file LICENSE 26 | URL: https://rconsortium.github.io/S7/, https://github.com/RConsortium/S7 27 | BugReports: https://github.com/RConsortium/S7/issues 28 | Depends: 29 | R (>= 3.5.0) 30 | Imports: 31 | utils 32 | Suggests: 33 | bench, 34 | callr, 35 | covr, 36 | knitr, 37 | methods, 38 | rmarkdown, 39 | testthat (>= 3.2.0), 40 | tibble 41 | VignetteBuilder: 42 | knitr 43 | Config/build/compilation-database: true 44 | Config/Needs/website: sloop 45 | Config/testthat/edition: 3 46 | Config/testthat/parallel: TRUE 47 | Config/testthat/start-first: external-generic 48 | Encoding: UTF-8 49 | Roxygen: list(markdown = TRUE) 50 | RoxygenNote: 7.3.2 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: S7 authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2021 S7 authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method("$",S7_object) 4 | S3method("$<-",S7_object) 5 | S3method("@<-",S7_object) 6 | S3method("[",S7_object) 7 | S3method("[<-",S7_object) 8 | S3method("[[",S7_object) 9 | S3method("[[<-",S7_object) 10 | S3method("|",S7_class) 11 | S3method(Ops,S7_object) 12 | S3method(Ops,S7_super) 13 | S3method(c,S7_class) 14 | S3method(print,S7_S3_class) 15 | S3method(print,S7_any) 16 | S3method(print,S7_base_class) 17 | S3method(print,S7_class) 18 | S3method(print,S7_external_generic) 19 | S3method(print,S7_generic) 20 | S3method(print,S7_method) 21 | S3method(print,S7_missing) 22 | S3method(print,S7_object) 23 | S3method(print,S7_property) 24 | S3method(print,S7_super) 25 | S3method(print,S7_union) 26 | S3method(str,S7_S3_class) 27 | S3method(str,S7_any) 28 | S3method(str,S7_base_class) 29 | S3method(str,S7_class) 30 | S3method(str,S7_missing) 31 | S3method(str,S7_object) 32 | S3method(str,S7_property) 33 | S3method(str,S7_super) 34 | S3method(str,S7_union) 35 | export("S7_data<-") 36 | export("method<-") 37 | export("prop<-") 38 | export("props<-") 39 | export(S4_register) 40 | export(S7_class) 41 | export(S7_data) 42 | export(S7_dispatch) 43 | export(S7_inherits) 44 | export(S7_object) 45 | export(as_class) 46 | export(check_is_S7) 47 | export(class_Date) 48 | export(class_POSIXct) 49 | export(class_POSIXlt) 50 | export(class_POSIXt) 51 | export(class_any) 52 | export(class_atomic) 53 | export(class_call) 54 | export(class_character) 55 | export(class_complex) 56 | export(class_data.frame) 57 | export(class_double) 58 | export(class_environment) 59 | export(class_expression) 60 | export(class_factor) 61 | export(class_formula) 62 | export(class_function) 63 | export(class_integer) 64 | export(class_language) 65 | export(class_list) 66 | export(class_logical) 67 | export(class_missing) 68 | export(class_name) 69 | export(class_numeric) 70 | export(class_raw) 71 | export(class_vector) 72 | export(convert) 73 | export(method) 74 | export(method_explain) 75 | export(methods_register) 76 | export(new_S3_class) 77 | export(new_class) 78 | export(new_external_generic) 79 | export(new_generic) 80 | export(new_object) 81 | export(new_property) 82 | export(new_union) 83 | export(prop) 84 | export(prop_exists) 85 | export(prop_names) 86 | export(props) 87 | export(set_props) 88 | export(super) 89 | export(valid_eventually) 90 | export(valid_implicitly) 91 | export(validate) 92 | if (getRversion() < "4.3.0") export(`@`) 93 | if (getRversion() >= "4.3.0" && !is.null(asNamespace("utils")$.AtNames)) S3method(utils::.AtNames,S7_object) 94 | if (getRversion() >= "4.3.0") S3method(base::`@`, S7_object) 95 | if (getRversion() >= "4.3.0") S3method(chooseOpsMethod, S7_object) 96 | if (getRversion() >= "4.3.0") S3method(chooseOpsMethod, S7_super) 97 | if (getRversion() >= "4.3.0") S3method(matrixOps, S7_object) 98 | if (getRversion() >= "4.3.0") S3method(matrixOps, S7_super) 99 | if (getRversion() >= "4.3.0") S3method(nameOfClass, S7_class, S7_class_name) 100 | if (getRversion() >= "4.3.0") S3method(nameOfClass,S7_base_class) 101 | importFrom(stats,setNames) 102 | importFrom(utils,getFromNamespace) 103 | importFrom(utils,globalVariables) 104 | importFrom(utils,hasName) 105 | importFrom(utils,head) 106 | importFrom(utils,packageName) 107 | importFrom(utils,str) 108 | useDynLib(S7, .registration = TRUE) 109 | -------------------------------------------------------------------------------- /R/S7-package.R: -------------------------------------------------------------------------------- 1 | ## usethis namespace: start 2 | #' @importFrom utils globalVariables 3 | #' @importFrom utils head str hasName 4 | #' @useDynLib S7, .registration = TRUE 5 | ## usethis namespace: end 6 | NULL 7 | -------------------------------------------------------------------------------- /R/aaa.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | `%||%` <- function(x, y) if (is.null(x)) y else x 4 | 5 | new_function <- function(args = NULL, 6 | body = NULL, 7 | env = asNamespace("S7")) { 8 | as.function.default(c(args, body) %||% list(NULL), env) 9 | } 10 | 11 | 12 | `append<-` <- function(x, after, value) { 13 | if (missing(after)) 14 | c(x, value) 15 | else 16 | append(x, value, after = after) 17 | } 18 | 19 | `append1<-` <- function (x, value) { 20 | stopifnot(is.list(x) || identical(mode(x), mode(value))) 21 | x[[length(x) + 1L]] <- value 22 | x 23 | } 24 | 25 | 26 | topNamespaceName <- function(env = parent.frame()) { 27 | env <- topenv(env) 28 | if (!isNamespace(env)) { 29 | return() # print visible 30 | } 31 | 32 | as.character(getNamespaceName(env)) # unname 33 | } 34 | 35 | is_string <- function(x) { 36 | identical(class(x), "character") && length(x) == 1L && !is.na(x) && x != "" 37 | } 38 | -------------------------------------------------------------------------------- /R/compatibility.R: -------------------------------------------------------------------------------- 1 | # Where needed, attach an environment containing @ that works with S7 2 | activate_backward_compatiblility <- function() { 3 | if (getRversion() < "4.3.0" && !"S7_at" %in% search()) { 4 | args <- list(list("@" = `@`), name = "S7_at", warn.conflicts = FALSE) 5 | do.call("attach", args) 6 | } 7 | invisible() 8 | } 9 | 10 | #' @aliases @ 11 | #' @usage NULL 12 | #' @rawNamespace if (getRversion() < "4.3.0") export(`@`) 13 | #' @name prop 14 | `@` <- function(object, name) { 15 | if (inherits(object, "S7_object")) { 16 | name <- as.character(substitute(name)) 17 | prop(object, name) 18 | } else { 19 | name <- substitute(name) 20 | do.call(base::`@`, list(object, name)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /R/constructor.R: -------------------------------------------------------------------------------- 1 | new_constructor <- function(parent, properties, 2 | envir = asNamespace("S7"), package = NULL) { 3 | properties <- as_properties(properties) 4 | arg_info <- constructor_args(parent, properties, envir, package) 5 | self_args <- as_names(names(arg_info$self), named = TRUE) 6 | 7 | if (identical(parent, S7_object) || (is_class(parent) && parent@abstract)) { 8 | new_object_call <- 9 | if (has_S7_symbols(envir, "new_object", "S7_object")) { 10 | bquote(new_object(S7_object(), ..(self_args)), splice = TRUE) 11 | } else { 12 | bquote(S7::new_object(S7::S7_object(), ..(self_args)), splice = TRUE) 13 | } 14 | 15 | return(new_function( 16 | args = arg_info$self, 17 | body = as.call(c(quote(`{`), 18 | # Force all promises here so that any errors are signaled from 19 | # the constructor() call instead of the new_object() call. 20 | unname(self_args), 21 | new_object_call 22 | )), 23 | env = envir 24 | )) 25 | } 26 | 27 | if (is_class(parent)) { 28 | parent_name <- parent@name 29 | parent_fun <- parent 30 | args <- modify_list(arg_info$parent, arg_info$self) 31 | } else if (is_base_class(parent)) { 32 | parent_name <- parent$constructor_name 33 | parent_fun <- parent$constructor 34 | args <- modify_list(arg_info$parent, arg_info$self) 35 | } else if (is_S3_class(parent)) { 36 | parent_name <- paste0("new_", parent$class[[1]]) 37 | parent_fun <- parent$constructor 38 | args <- formals(parent$constructor) 39 | args[names(arg_info$self)] <- arg_info$self 40 | } else { 41 | # user facing error in S7_class() 42 | stop("Unsupported `parent` type", call. = FALSE) 43 | } 44 | 45 | # ensure default value for `...` is empty 46 | if ("..." %in% names(args)) { 47 | args[names(args) == "..."] <- list(quote(expr = )) 48 | } 49 | 50 | parent_args <- as_names(names(arg_info$parent), named = TRUE) 51 | names(parent_args)[names(parent_args) == "..."] <- "" 52 | parent_call <- new_call(parent_name, parent_args) 53 | body <- new_call( 54 | if (has_S7_symbols(envir, "new_object")) "new_object" else c("S7", "new_object"), 55 | c(parent_call, self_args) 56 | ) 57 | 58 | env <- new.env(parent = envir) 59 | env[[parent_name]] <- parent_fun 60 | 61 | new_function(args, body, env) 62 | } 63 | 64 | constructor_args <- function(parent, properties = list(), 65 | envir = asNamespace("S7"), package = NULL) { 66 | parent_args <- formals(class_constructor(parent)) 67 | 68 | # Remove read-only properties 69 | properties <- properties[!vlapply(properties, prop_is_read_only)] 70 | 71 | self_arg_nms <- names2(properties) 72 | 73 | if (is_class(parent) && !parent@abstract) { 74 | # Remove any parent properties; can't use parent_args() since the constructor 75 | # might automatically set some properties. 76 | self_arg_nms <- setdiff(self_arg_nms, names2(parent@properties)) 77 | } 78 | 79 | self_args <- as.pairlist(lapply( 80 | setNames(, self_arg_nms), 81 | function(name) prop_default(properties[[name]], envir, package)) 82 | ) 83 | 84 | list(parent = parent_args, 85 | self = self_args) 86 | } 87 | 88 | 89 | # helpers ----------------------------------------------------------------- 90 | 91 | is_property_dynamic <- function(x) is.function(x$getter) 92 | 93 | missing_args <- function(names) { 94 | lapply(setNames(, names), function(i) quote(class_missing)) 95 | } 96 | 97 | new_call <- function(call, args) { 98 | if (is.character(call)) { 99 | call <- switch(length(call), 100 | as.name(call), 101 | as.call(c(quote(`::`), lapply(call, as.name)))) 102 | } 103 | as.call(c(list(call), args)) 104 | } 105 | 106 | as_names <- function(x, named = FALSE) { 107 | if (named) { 108 | names(x) <- x 109 | } 110 | lapply(x, as.name) 111 | } 112 | 113 | has_S7_symbols <- function(env, ...) { 114 | env <- topenv(env) 115 | if (identical(env, asNamespace("S7"))) 116 | return (TRUE) 117 | if (!isNamespace(env)) 118 | return (FALSE) 119 | imports <- getNamespaceImports(env)[["S7"]] 120 | symbols <- c(...) %||% getNamespaceExports("S7") 121 | all(symbols %in% imports) 122 | } 123 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Get/set underlying "base" data 2 | #' 3 | #' When an S7 class inherits from an existing base type, it can be useful 4 | #' to work with the underlying object, i.e. the S7 object stripped of class 5 | #' and properties. 6 | #' 7 | #' @inheritParams prop 8 | #' @param value Object used to replace the underlying data. 9 | #' @return `S7_data()` returns the data stored in the base object; 10 | #' `S7_data<-()` is called for its side-effects and returns `object` 11 | #' invisibly. 12 | #' @export 13 | #' @examples 14 | #' Text <- new_class("Text", parent = class_character) 15 | #' y <- Text(c(foo = "bar")) 16 | #' y 17 | #' S7_data(y) 18 | #' 19 | #' S7_data(y) <- c("a", "b") 20 | #' y 21 | S7_data <- function(object) { 22 | check_is_S7(object) 23 | 24 | zap_attr(object, c(prop_names(object), "class", "S7_class")) 25 | } 26 | 27 | #' @export 28 | #' @rdname S7_data 29 | `S7_data<-` <- function(object, check = TRUE, value) { 30 | attrs <- attributes(object) 31 | object <- value 32 | attributes(object) <- attrs 33 | if (isTRUE(check)) { 34 | validate(object) 35 | } 36 | return(invisible(object)) 37 | } 38 | 39 | 40 | zap_attr <- function(x, names) { 41 | for (name in names) { 42 | attr(x, name) <- NULL 43 | } 44 | x 45 | } 46 | -------------------------------------------------------------------------------- /R/generic-spec.R: -------------------------------------------------------------------------------- 1 | is_generic <- function(x) { 2 | is_S7_generic(x) || is_external_generic(x) || is_S3_generic(x) || is_S4_generic(x) 3 | } 4 | 5 | as_generic <- function(x) { 6 | if (is_generic(x)) { 7 | x 8 | } else if (is.function(x)) { 9 | as_S3_generic(x) 10 | } else { 11 | msg <- sprintf("`generic` must be a function, not a %s", obj_desc(x)) 12 | stop(msg, call. = FALSE) 13 | } 14 | } 15 | 16 | as_S3_generic <- function(x) { 17 | use_method <- find_call(body(x), quote(UseMethod)) 18 | if (!is.null(use_method)) { 19 | return(S3_generic(x, as.character(use_method[[2]]))) 20 | } else { 21 | name <- find_base_name(x) 22 | if (name %in% names(base_ops)) { 23 | return(base_ops[[name]]) 24 | } else if (name %in% names(base_matrix_ops)) { 25 | return(base_matrix_ops[[name]]) 26 | } else if (!is.na(name) && is_internal_generic(name)) { 27 | return(S3_generic(x, name)) 28 | } 29 | } 30 | 31 | stop("`generic` is a function, but not an S3 generic function: \n", 32 | deparse_trunc(x, 100), call. = FALSE) 33 | } 34 | 35 | S3_generic <- function(generic, name) { 36 | out <- list(generic = generic, name = name) 37 | class(out) <- "S7_S3_generic" 38 | out 39 | } 40 | 41 | is_S3_generic <- function(x) inherits(x, "S7_S3_generic") 42 | 43 | is_S4_generic <- function(x) inherits(x, "genericFunction") 44 | 45 | # Is the generic defined in the "current" package 46 | is_local_generic <- function(generic, package) { 47 | if (is_external_generic(generic)) { 48 | return(FALSE) 49 | } 50 | 51 | generic_pkg <- package_name(generic) 52 | is.null(generic_pkg) || generic_pkg == package 53 | } 54 | 55 | package_name <- function(f) { 56 | env <- environment(f) 57 | if (is.null(env)) { 58 | "base" 59 | } else { 60 | (packageName(env)) 61 | } 62 | } 63 | 64 | generic_n_dispatch <- function(x) { 65 | if (is_S7_generic(x)) { 66 | length(x@dispatch_args) 67 | } else if (is_external_generic(x)) { 68 | length(x$dispatch_args) 69 | } else if (is_S3_generic(x)) { 70 | 1 71 | } else if (is_S4_generic(x)) { 72 | length(x@signature) 73 | } else { 74 | stop(sprintf("Invalid input %", obj_desc(x)), call. = FALSE) 75 | } 76 | } 77 | 78 | # Internal generics ------------------------------------------------------- 79 | 80 | find_base_name <- function(f, candidates = NULL) { 81 | env <- baseenv() 82 | candidates <- candidates %||% names(env) 83 | for (name in candidates) { 84 | if (identical(f, env[[name]])) { 85 | return(name) 86 | } 87 | } 88 | 89 | NA 90 | } 91 | 92 | is_internal_generic <- function(x) { 93 | x %in% internal_generics() 94 | } 95 | 96 | internal_generics <- function() { 97 | group <- unlist(group_generics(), use.names = FALSE) 98 | primitive <- .S3PrimitiveGenerics 99 | 100 | # Extracted from ?"internal generic" 101 | internal <- c("[", "[[", "$", "[<-", "[[<-", "$<-", "unlist", 102 | "cbind", "rbind", "as.vector") 103 | 104 | c(group, primitive, internal) 105 | } 106 | 107 | group_generics <- function() { 108 | # S3 group generics can be defined by combining S4 group generics 109 | groups <- list( 110 | Ops = c("Arith", "Compare", "Logic"), 111 | Math = c("Math", "Math2"), 112 | Summary = "Summary", 113 | Complex = "Complex" 114 | ) 115 | 116 | out <- lapply(groups, function(x) unlist(lapply(x, methods::getGroupMembers))) 117 | if (getRversion() >= "4.3") { 118 | out$matrixOps <- c("%*%") 119 | } 120 | out 121 | } 122 | -------------------------------------------------------------------------------- /R/inherits.R: -------------------------------------------------------------------------------- 1 | #' Does this object inherit from an S7 class? 2 | #' 3 | #' * `S7_inherits()` returns `TRUE` or `FALSE`. 4 | #' * `check_is_S7()` throws an error if `x` isn't the specified `class`. 5 | #' 6 | #' @param x An object 7 | #' @param class An S7 class or `NULL`. If `NULL`, tests whether `x` is an 8 | #' S7 object without testing for a specific class. 9 | #' @param arg Argument name used in error message. 10 | #' @returns 11 | #' * `S7_inherits()` returns a single `TRUE` or `FALSE`. 12 | #' * `check_is_S7()` returns nothing; it's called for its side-effects. 13 | #' 14 | #' @note Starting with \R 4.3.0, `base::inherits()` can accept an S7 class as 15 | #' the second argument, supporting usage like `inherits(x, Foo)`. 16 | #' @export 17 | #' @examples 18 | #' Foo1 <- new_class("Foo1") 19 | #' Foo2 <- new_class("Foo2") 20 | #' 21 | #' S7_inherits(Foo1(), Foo1) 22 | #' check_is_S7(Foo1()) 23 | #' check_is_S7(Foo1(), Foo1) 24 | #' 25 | #' S7_inherits(Foo1(), Foo2) 26 | #' try(check_is_S7(Foo1(), Foo2)) 27 | #' 28 | #' if (getRversion() >= "4.3.0") 29 | #' inherits(Foo1(), Foo1) 30 | S7_inherits <- function(x, class = NULL) { 31 | if (!(is.null(class) || inherits(class, "S7_class"))) { 32 | stop("`class` must be an or NULL") 33 | } 34 | 35 | inherits(x, "S7_object") && 36 | (is.null(class) || inherits(x, S7_class_name(class))) 37 | } 38 | 39 | #' @export 40 | #' @rdname S7_inherits 41 | # called from src/prop.c 42 | check_is_S7 <- function(x, class = NULL, arg = deparse(substitute(x))) { 43 | if (S7_inherits(x, class)) { 44 | return(invisible()) 45 | } 46 | 47 | msg <- sprintf( 48 | "`%s` must be %s, not a %s", 49 | arg, 50 | if (is.null(class)) "an " else paste0("a ", class_desc(class)), 51 | obj_desc(x) 52 | ) 53 | stop(msg, call. = FALSE) 54 | } 55 | -------------------------------------------------------------------------------- /R/method-dispatch.R: -------------------------------------------------------------------------------- 1 | # Called from C 2 | method_lookup_error <- function(name, args) { 3 | types <- vcapply(args, obj_desc) 4 | msg <- method_lookup_error_message(name, types) 5 | cnd <- errorCondition(msg, class = c("S7_error_method_not_found", "error")) 6 | stop(cnd) 7 | } 8 | 9 | method_lookup_error_message <- function(name, types) { 10 | if (length(types) == 1) { 11 | sprintf("Can't find method for `%s(%s)`.", name, types) 12 | } else { 13 | arg_names <- paste0(names(types), collapse = ", ") 14 | types <- paste0("- ", format(names(types)), ": ", types, collapse = "\n") 15 | sprintf("Can't find method for generic `%s(%s)`:\n%s", name, arg_names, types) 16 | } 17 | } 18 | 19 | #' @rdname new_generic 20 | #' @order 2 21 | #' @export 22 | S7_dispatch <- function() { 23 | .External2(method_call_, sys.function(-1L), sys.frame(-1L)) 24 | } 25 | -------------------------------------------------------------------------------- /R/method-ops.R: -------------------------------------------------------------------------------- 1 | base_ops <- NULL 2 | base_matrix_ops <- NULL 3 | 4 | on_load_define_ops <- function() { 5 | base_ops <<- lapply( 6 | setNames(, group_generics()$Ops), 7 | new_generic, 8 | dispatch_args = c("e1", "e2") 9 | ) 10 | base_matrix_ops <<- lapply( 11 | setNames(, group_generics()$matrixOps), 12 | new_generic, 13 | dispatch_args = c("x", "y") 14 | ) 15 | } 16 | 17 | #' @export 18 | Ops.S7_object <- function(e1, e2) { 19 | cnd <- tryCatch( 20 | return(base_ops[[.Generic]](e1, e2)), 21 | S7_error_method_not_found = function(cnd) cnd 22 | ) 23 | 24 | if (S7_inherits(e1) && S7_inherits(e2)) { 25 | stop(cnd) 26 | } else { 27 | # Must call NextMethod() directly in the method, not wrapped in an 28 | # anonymous function. 29 | NextMethod() 30 | } 31 | } 32 | 33 | #' @rawNamespace if (getRversion() >= "4.3.0") S3method(chooseOpsMethod, S7_object) 34 | chooseOpsMethod.S7_object <- function(x, y, mx, my, cl, reverse) TRUE 35 | 36 | #' @rawNamespace if (getRversion() >= "4.3.0") S3method(matrixOps, S7_object) 37 | matrixOps.S7_object <- function(x, y) { 38 | base_matrix_ops[[.Generic]](x, y) 39 | } 40 | 41 | #' @export 42 | Ops.S7_super <- Ops.S7_object 43 | 44 | #' @rawNamespace if (getRversion() >= "4.3.0") S3method(chooseOpsMethod, S7_super) 45 | chooseOpsMethod.S7_super <- chooseOpsMethod.S7_object 46 | 47 | #' @rawNamespace if (getRversion() >= "4.3.0") S3method(matrixOps, S7_super) 48 | matrixOps.S7_super <- matrixOps.S7_object 49 | -------------------------------------------------------------------------------- /R/special.R: -------------------------------------------------------------------------------- 1 | #' Dispatch on a missing argument 2 | #' 3 | #' Use `class_missing` to dispatch when the user has not supplied an argument, 4 | #' i.e. it's missing in the sense of [missing()], not in the sense of 5 | #' [is.na()]. 6 | #' 7 | #' @export 8 | #' @return Sentinel objects used for special types of dispatch. 9 | #' @format NULL 10 | #' @examples 11 | #' foo <- new_generic("foo", "x") 12 | #' method(foo, class_numeric) <- function(x) "number" 13 | #' method(foo, class_missing) <- function(x) "missing" 14 | #' method(foo, class_any) <- function(x) "fallback" 15 | #' 16 | #' foo(1) 17 | #' foo() 18 | #' foo("") 19 | class_missing <- structure(list(), class = "S7_missing") 20 | 21 | is_class_missing <- function(x) inherits(x, "S7_missing") 22 | 23 | #' @export 24 | print.S7_missing <- function(x, ...) { 25 | cat("\n") 26 | invisible(x) 27 | } 28 | #' @export 29 | str.S7_missing <- function(object, ..., nest.lev = 0) { 30 | cat(if (nest.lev > 0) " ") 31 | print(object) 32 | } 33 | 34 | #' Dispatch on any class 35 | #' 36 | #' Use `class_any` to register a default method that is called when no other 37 | #' methods are matched. 38 | #' 39 | #' @export 40 | #' @format NULL 41 | #' @examples 42 | #' foo <- new_generic("foo", "x") 43 | #' method(foo, class_numeric) <- function(x) "number" 44 | #' method(foo, class_any) <- function(x) "fallback" 45 | #' 46 | #' foo(1) 47 | #' foo("x") 48 | class_any <- structure(list(), class = "S7_any") 49 | 50 | is_class_any <- function(x) inherits(x, "S7_any") 51 | 52 | #' @export 53 | print.S7_any <- function(x, ...) { 54 | cat("\n") 55 | invisible(x) 56 | } 57 | #' @export 58 | str.S7_any <- function(object, ..., nest.lev = 0) { 59 | cat(if (nest.lev > 0) " ") 60 | print(object) 61 | } 62 | -------------------------------------------------------------------------------- /R/super.R: -------------------------------------------------------------------------------- 1 | #' Force method dispatch to use a superclass 2 | #' 3 | #' @description 4 | #' `super(from, to)` causes the dispatch for the next generic to use the method 5 | #' for the superclass `to` instead of the actual class of `from`. It's needed 6 | #' when you want to implement a method in terms of the implementation of its 7 | #' superclass. 8 | #' 9 | #' ## S3 & S4 10 | #' `super()` performs a similar role to [NextMethod()] in S3 or 11 | #' [methods::callNextMethod()] in S4, but is much more explicit: 12 | #' 13 | #' * The super class that `super()` will use is known when write `super()` 14 | #' (i.e. statically) as opposed to when the generic is called 15 | #' (i.e. dynamically). 16 | #' 17 | #' * All arguments to the generic are explicit; they are not automatically 18 | #' passed along. 19 | #' 20 | #' This makes `super()` more verbose, but substantially easier to 21 | #' understand and reason about. 22 | #' 23 | #' ## `super()` in S3 generics 24 | #' 25 | #' Note that you can't use `super()` in methods for an S3 generic. 26 | #' For example, imagine that you have made a subclass of "integer": 27 | #' 28 | #' ```{r} 29 | #' MyInt <- new_class("MyInt", parent = class_integer, package = NULL) 30 | #' ``` 31 | #' 32 | #' Now you go to write a custom print method: 33 | #' 34 | #' ```{r} 35 | #' method(print, MyInt) <- function(x, ...) { 36 | #' cat("") 37 | #' print(super(x, to = class_integer)) 38 | #' } 39 | #' 40 | #' MyInt(10L) 41 | #' ``` 42 | #' 43 | #' This doesn't work because `print()` isn't an S7 generic so doesn't 44 | #' understand how to interpret the special object that `super()` produces. 45 | #' While you could resolve this problem with [NextMethod()] (because S7 is 46 | #' implemented on top of S3), we instead recommend using [S7_data()] to extract 47 | #' the underlying base object: 48 | #' 49 | #' ```{r} 50 | #' method(print, MyInt) <- function(x, ...) { 51 | #' cat("") 52 | #' print(S7_data(x)) 53 | #' } 54 | #' 55 | #' MyInt(10L) 56 | #' ``` 57 | #' 58 | #' @param from An S7 object to cast. 59 | #' @param to An S7 class specification, passed to [as_class()]. Must be a 60 | #' superclass of `object`. 61 | #' @returns An `S7_super` object which should always be passed 62 | #' immediately to a generic. It has no other special behavior. 63 | #' @export 64 | #' @examples 65 | #' Foo1 <- new_class("Foo1", properties = list(x = class_numeric, y = class_numeric)) 66 | #' Foo2 <- new_class("Foo2", Foo1, properties = list(z = class_numeric)) 67 | #' 68 | #' total <- new_generic("total", "x") 69 | #' method(total, Foo1) <- function(x) x@x + x@y 70 | #' 71 | #' # This won't work because it'll be stuck in an infinite loop: 72 | #' method(total, Foo2) <- function(x) total(x) + x@z 73 | #' 74 | #' # We could write 75 | #' method(total, Foo2) <- function(x) x@x + x@y + x@z 76 | #' # but then we'd need to remember to update it if the implementation 77 | #' # for total() ever changed. 78 | #' 79 | #' # So instead we use `super()` to call the method for the parent class: 80 | #' method(total, Foo2) <- function(x) total(super(x, to = Foo1)) + x@z 81 | #' total(Foo2(1, 2, 3)) 82 | #' 83 | #' # To see the difference between convert() and super() we need a 84 | #' # method that calls another generic 85 | #' 86 | #' bar1 <- new_generic("bar1", "x") 87 | #' method(bar1, Foo1) <- function(x) 1 88 | #' method(bar1, Foo2) <- function(x) 2 89 | #' 90 | #' bar2 <- new_generic("bar2", "x") 91 | #' method(bar2, Foo1) <- function(x) c(1, bar1(x)) 92 | #' method(bar2, Foo2) <- function(x) c(2, bar1(x)) 93 | #' 94 | #' obj <- Foo2(1, 2, 3) 95 | #' bar2(obj) 96 | #' # convert() affects every generic: 97 | #' bar2(convert(obj, to = Foo1)) 98 | #' # super() only affects the _next_ call to a generic: 99 | #' bar2(super(obj, to = Foo1)) 100 | super <- function(from, to) { 101 | check_is_S7(from) 102 | 103 | to <- as_class(to) 104 | check_can_inherit(to) 105 | if (!class_inherits(from, to)) { 106 | msg <- sprintf( 107 | "%s doesn't inherit from %s", 108 | obj_desc(from), 109 | class_desc(to) 110 | ) 111 | stop(msg) 112 | } 113 | 114 | # Must not change order of these fields as C code indexes by position 115 | structure( 116 | list( 117 | object = from, 118 | dispatch = class_dispatch(to) 119 | ), 120 | class = "S7_super" 121 | ) 122 | } 123 | 124 | #' @export 125 | print.S7_super <- function(x, ...) { 126 | str(x, ...) 127 | invisible(x) 128 | } 129 | #' @export 130 | str.S7_super <- function(object, ..., nest.lev = 0) { 131 | cat(if (nest.lev > 0) " ") 132 | cat("super(", obj_desc(object$object), ", <", object$dispatch[[1]], ">)", sep = "") 133 | } 134 | -------------------------------------------------------------------------------- /R/union.R: -------------------------------------------------------------------------------- 1 | #' Define a class union 2 | #' 3 | #' @description 4 | #' A class union represents a list of possible classes. You can create it 5 | #' with `new_union(a, b, c)` or `a | b | c`. Unions can be used in two 6 | #' places: 7 | #' 8 | #' * To allow a property to be one of a set of classes, 9 | #' `new_property(class_integer | Range)`. The default `default` value for the 10 | #' property will be the constructor of the first object in the union. 11 | #' This means if you want to create an "optional" property (i.e. one that 12 | #' can be `NULL` or of a specified type), you'll need to write (e.g.) 13 | #' `NULL | class_integer`. 14 | #' 15 | #' * As a convenient short-hand to define methods for multiple classes. 16 | #' `method(foo, X | Y) <- f` is short-hand for 17 | #' `method(foo, X) <- f; method(foo, Y) <- foo` 18 | #' 19 | #' S7 includes built-in unions for "numeric" (integer and double vectors), 20 | #' "atomic" (logical, numeric, complex, character, and raw vectors) and 21 | #' "vector" (atomic vectors, lists, and expressions). 22 | #' 23 | #' @param ... The classes to include in the union. See [as_class()] for 24 | #' details. 25 | #' @return An S7 union, i.e. a list with class `S7_union`. 26 | #' @export 27 | #' @examples 28 | #' logical_or_character <- new_union(class_logical, class_character) 29 | #' logical_or_character 30 | #' # or with shortcut syntax 31 | #' logical_or_character <- class_logical | class_character 32 | #' 33 | #' Foo <- new_class("Foo", properties = list(x = logical_or_character)) 34 | #' Foo(x = TRUE) 35 | #' Foo(x = letters[1:5]) 36 | #' try(Foo(1:3)) 37 | #' 38 | #' bar <- new_generic("bar", "x") 39 | #' # Use built-in union 40 | #' method(bar, class_atomic) <- function(x) "Hi!" 41 | #' bar 42 | #' bar(TRUE) 43 | #' bar(letters) 44 | #' try(bar(NULL)) 45 | new_union <- function(...) { 46 | classes <- class_flatten(list(...)) 47 | out <- list(classes = classes) 48 | class(out) <- "S7_union" 49 | out 50 | } 51 | 52 | #' @export 53 | `|.S7_class` <- function(e1, e2) { 54 | new_union(e1, e2) 55 | } 56 | # Register remaining methods onLoad so that their pointers are identical, 57 | # working around a bug that was fixed in R 4.1: 58 | # https://github.com/wch/r-source/commit/b41344e3d0da7d78fd 59 | on_load_define_or_methods <- function() { 60 | registerS3method("|", "S7_union", `|.S7_class`) 61 | registerS3method("|", "S7_base_class", `|.S7_class`) 62 | registerS3method("|", "S7_S3_class", `|.S7_class`) 63 | registerS3method("|", "S7_any", `|.S7_class`) 64 | registerS3method("|", "S7_missing", `|.S7_class`) 65 | registerS3method("|", "classGeneratorFunction", `|.S7_class`) 66 | registerS3method("|", "ClassUnionRepresentation", `|.S7_class`) 67 | registerS3method("|", "classRepresentation", `|.S7_class`) 68 | } 69 | 70 | is_union <- function(x) inherits(x, "S7_union") 71 | 72 | #' @export 73 | print.S7_union <- function(x, ...) { 74 | cat(": ", class_desc(x), "\n", sep = "") 75 | invisible(x) 76 | } 77 | 78 | #' @export 79 | str.S7_union <- function(object, ..., nest.lev = 0) { 80 | cat(if (nest.lev > 0) " ") 81 | print(object) 82 | } 83 | 84 | class_flatten <- function(x) { 85 | x <- lapply(x, as_class) 86 | 87 | # Flatten unions 88 | is_union <- vlapply(x, is_union) 89 | x[!is_union] <- lapply(x[!is_union], list) 90 | x[is_union] <- lapply(x[is_union], function(x) x$classes) 91 | 92 | unique(unlist(x, recursive = FALSE, use.names = FALSE)) 93 | } 94 | 95 | # See .onLoad() for definition 96 | base_unions <- list() 97 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | #' Base S7 class 2 | #' 3 | #' The base class from which all S7 classes eventually inherit from. 4 | #' 5 | #' @keywords internal 6 | #' @export 7 | #' @return The base S7 object. 8 | #' @examples 9 | #' 10 | #' S7_object 11 | S7_object <- new_class( 12 | name = "S7_object", 13 | package = NULL, 14 | parent = NULL, 15 | constructor = function() { 16 | .Call(S7_object_) 17 | }, 18 | validator = function(self) { 19 | if (!is_S7_type(self)) { 20 | "Underlying data is corrupt" 21 | } 22 | } 23 | ) 24 | methods::setOldClass("S7_object") 25 | 26 | .S7_type <- NULL 27 | # Defined onLoad because it depends on R version 28 | on_load_define_S7_type <- function() { 29 | .S7_type <<- typeof(.Call(S7_object_)) 30 | } 31 | is_S7_type <- function(x) { 32 | typeof(x) == .S7_type 33 | } 34 | 35 | #' @export 36 | `$.S7_object` <- function(x, name) { 37 | if (typeof(x) %in% c("list", "environment")) { 38 | NextMethod() 39 | } else { 40 | msg <- sprintf( 41 | "Can't get S7 properties with `$`. Did you mean `%s@%s`?", 42 | deparse1(substitute(x)), 43 | name 44 | ) 45 | stop(msg, call. = FALSE) 46 | } 47 | } 48 | #' @export 49 | `$<-.S7_object` <- function(x, name, value) { 50 | if (typeof(x) %in% c("list", "environment")) { 51 | NextMethod() 52 | } else { 53 | msg <- sprintf( 54 | "Can't set S7 properties with `$`. Did you mean `...@%s <- %s`?", 55 | name, 56 | deparse1(substitute(value)) 57 | ) 58 | stop(msg, call. = FALSE) 59 | } 60 | } 61 | 62 | #' @export 63 | `[.S7_object` <- function(x, ..., drop = TRUE) { 64 | check_subsettable(x) 65 | NextMethod() 66 | } 67 | #' @export 68 | `[<-.S7_object` <- function(x, ..., value) { 69 | check_subsettable(x) 70 | NextMethod() 71 | } 72 | 73 | #' @export 74 | `[[.S7_object` <- function(x, ...) { 75 | check_subsettable(x, allow_env = TRUE) 76 | NextMethod() 77 | } 78 | #' @export 79 | `[[<-.S7_object` <- function(x, ..., value) { 80 | check_subsettable(x, allow_env = TRUE) 81 | NextMethod() 82 | } 83 | 84 | check_subsettable <- function(x, allow_env = FALSE) { 85 | allowed_types <- c("list", "language", "pairlist", if (allow_env) "environment") 86 | if (!typeof(x) %in% allowed_types) { 87 | stop("S7 objects are not subsettable.") 88 | } 89 | invisible(TRUE) 90 | } 91 | 92 | S7_generic <- NULL 93 | 94 | on_load_define_S7_generic <- function() { 95 | # we do this in .onLoad() because dynlib `prop_` symbol 96 | # is not available at pkg build time, and new_class() 97 | # errors if `@` is not usable. 98 | S7_generic <<- new_class( 99 | name = "S7_generic", 100 | package = NULL, 101 | properties = list( 102 | name = class_character, 103 | methods = class_environment, 104 | dispatch_args = class_character 105 | ), 106 | parent = class_function 107 | ) 108 | } 109 | 110 | methods::setOldClass(c("S7_generic", "function", "S7_object")) 111 | is_S7_generic <- function(x) inherits(x, "S7_generic") 112 | 113 | 114 | S7_method <- NULL 115 | 116 | on_load_define_S7_method <- function() { 117 | S7_method <<- new_class( 118 | "S7_method", 119 | package = NULL, 120 | parent = class_function, 121 | properties = list(generic = S7_generic, signature = class_list) 122 | ) 123 | } 124 | methods::setOldClass(c("S7_method", "function", "S7_object")) 125 | 126 | # hooks ------------------------------------------------------------------- 127 | 128 | .onAttach <- function(libname, pkgname) { 129 | env <- as.environment(paste0("package:", pkgname)) 130 | if (getRversion() < "4.3.0") { 131 | env[[".conflicts.OK"]] <- TRUE 132 | } 133 | } 134 | 135 | .onLoad <- function(...) { 136 | activate_backward_compatiblility() 137 | 138 | on_load_define_S7_generic() 139 | on_load_define_S7_method() 140 | on_load_make_convert_generic() 141 | on_load_define_ops() 142 | on_load_define_or_methods() 143 | on_load_define_S7_type() 144 | on_load_define_union_classes() 145 | } 146 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | 16 | # S7 17 | 18 | 19 | 20 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) 21 | [![R-CMD-check](https://github.com/RConsortium/S7/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/RConsortium/S7/actions/workflows/R-CMD-check.yaml) 22 | [![Codecov test coverage](https://codecov.io/gh/RConsortium/S7/branch/main/graph/badge.svg)](https://app.codecov.io/gh/RConsortium/S7?branch=main) 23 | 24 | 25 | The S7 package is a new OOP system designed to be a successor to S3 and S4. 26 | It has been designed and implemented collaboratively by the R Consortium Object-Oriented Programming Working Group, which includes representatives from R-Core, Bioconductor, the tidyverse/Posit, and the wider R community. 27 | 28 | S7 is somewhat experimental; we are confident in the design but it has relatively little usage in the wild currently. 29 | We hope to avoid any major breaking changes, but reserve the right if we discover major problems. 30 | 31 | ## Installation 32 | 33 | The long-term goal of this project is to merge S7 in to base R. 34 | For now, you can experiment by installing it from CRAN: 35 | 36 | ```{r, eval = FALSE} 37 | install.packages("S7") 38 | ``` 39 | 40 | ## Usage 41 | 42 | This section gives a very brief overview of the entirety of S7. 43 | Learn more of the basics in `vignette("S7")`, generics and methods in `vignette("generics-methods")`, classes and objects in `vignette("classes-objects")`, and compatibility with S3 and S4 in `vignette("compatibility")`. 44 | 45 | ```{r} 46 | library(S7) 47 | ``` 48 | 49 | ### Classes and objects 50 | 51 | S7 classes have a formal definition, which includes a list of properties and an optional validator. 52 | Use `new_class()` to define a class: 53 | 54 | ```{r} 55 | range <- new_class("range", 56 | properties = list( 57 | start = class_double, 58 | end = class_double 59 | ), 60 | validator = function(self) { 61 | if (length(self@start) != 1) { 62 | "@start must be length 1" 63 | } else if (length(self@end) != 1) { 64 | "@end must be length 1" 65 | } else if (self@end < self@start) { 66 | "@end must be greater than or equal to @start" 67 | } 68 | } 69 | ) 70 | ``` 71 | 72 | `new_class()` returns the class object, which is also the constructor you use to create instances of the class: 73 | 74 | ```{r} 75 | x <- range(start = 1, end = 10) 76 | x 77 | ``` 78 | 79 | ### Properties 80 | 81 | The data possessed by an object is called its **properties**. 82 | Use `@` to get and set properties: 83 | 84 | ```{r} 85 | x@start 86 | x@end <- 20 87 | x 88 | ``` 89 | 90 | Properties are automatically validated against the type declared in `new_class()` (`double` in this case), and with the class **validator**: 91 | 92 | ```{r, error = TRUE} 93 | x@end <- "x" 94 | x@end <- -1 95 | ``` 96 | 97 | ### Generics and methods 98 | 99 | Like S3 and S4, S7 uses **functional OOP** where methods belong to **generic** functions, and method calls look like all other function calls: `generic(object, arg2, arg3)`. 100 | This style is called functional because from the outside it looks like a regular function call, and internally the components are also functions. 101 | 102 | Use `new_generic()` to create a new generic: the first argument is the generic name (used in error messages) and the second gives the arguments used for dispatch. 103 | The third, and optional argument, supplies the body of the generic. 104 | This is only needed if your generic has additional arguments that aren't used for method dispatch. 105 | 106 | ```{r} 107 | inside <- new_generic("inside", "x") 108 | ``` 109 | 110 | Once you have a generic, you can define a method for a specific class with `method<-`: 111 | 112 | ```{r} 113 | # Add a method for our class 114 | method(inside, range) <- function(x, y) { 115 | y >= x@start & y <= x@end 116 | } 117 | 118 | inside(x, c(0, 5, 10, 15)) 119 | ``` 120 | 121 | You can use `method<-` to register methods for base types on S7 generics, and S7 classes on S3 or S4 generics. 122 | See `vignette("compatibility")` for more details. 123 | -------------------------------------------------------------------------------- /S7.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | 24 | MarkdownWrap: Sentence 25 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://rconsortium.github.io/S7/ 2 | 3 | template: 4 | bootstrap: 5 5 | 6 | reference: 7 | - title: Key functions 8 | contents: 9 | - new_class 10 | - new_generic 11 | - new_union 12 | - method<- 13 | - S7_inherits 14 | - validate 15 | 16 | - title: Properties 17 | contents: 18 | - new_property 19 | - prop 20 | - prop_names 21 | - props 22 | - S7_data 23 | 24 | - title: Method dispatch 25 | contents: 26 | - convert 27 | - class_missing 28 | - class_any 29 | - method 30 | - method_explain 31 | - super 32 | - S7_class 33 | 34 | - title: Packages 35 | desc: > 36 | Functions needed when using S7 within a package. See `vignette("packages")` 37 | for more details. 38 | contents: 39 | - methods_register 40 | - new_external_generic 41 | 42 | - title: Compatibility 43 | desc: > 44 | These tools provide a layer of compatibility between S7 and S3 classes, S4 45 | classes, and base types. See `vignette("compatibility")` for more details. 46 | contents: 47 | - base_classes 48 | - base_s3_classes 49 | - new_S3_class 50 | - S4_register 51 | 52 | articles: 53 | - title: Learn S7 54 | navbar: ~ 55 | contents: 56 | - S7 57 | - generics-methods 58 | - classes-objects 59 | - compatibility 60 | - packages 61 | - motivation 62 | - performance 63 | 64 | - title: Initial design and specification 65 | contents: 66 | - spec/proposal 67 | - spec/requirements 68 | - spec/design 69 | - spec/dispatch 70 | 71 | - title: Minutes 72 | contents: starts_with("minutes/") 73 | 74 | news: 75 | releases: 76 | - text: "S7 0.2.0" 77 | href: https://www.tidyverse.org/blog/2024/11/s7-0-2-0/ 78 | - text: "S7 0.1.0" 79 | href: https://blog.r-project.org/2024/05/17/generalizing-support-for-functional-oop-in-r/index.html 80 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## R CMD check results 2 | 3 | 0 errors | 0 warnings | 0 notes 4 | 5 | ## revdepcheck results 6 | 7 | We checked 4 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 8 | 9 | * We saw 1 new problem with the fr package, and we have provided a patch 10 | at https://github.com/cole-brokamp/fr/pull/15 11 | * We failed to check 0 packages 12 | -------------------------------------------------------------------------------- /man/S4_register.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/S4.R 3 | \name{S4_register} 4 | \alias{S4_register} 5 | \title{Register an S7 class with S4} 6 | \usage{ 7 | S4_register(class, env = parent.frame()) 8 | } 9 | \arguments{ 10 | \item{class}{An S7 class created with \code{\link[=new_class]{new_class()}}.} 11 | 12 | \item{env}{Expert use only. Environment where S4 class will be registered.} 13 | } 14 | \value{ 15 | Nothing; the function is called for its side-effect. 16 | } 17 | \description{ 18 | If you want to use \link{method<-} to register an method for an S4 generic with 19 | an S7 class, you need to call \code{S4_register()} once. 20 | } 21 | \examples{ 22 | methods::setGeneric("S4_generic", function(x) { 23 | standardGeneric("S4_generic") 24 | }) 25 | 26 | Foo <- new_class("Foo") 27 | S4_register(Foo) 28 | method(S4_generic, Foo) <- function(x) "Hello" 29 | 30 | S4_generic(Foo()) 31 | } 32 | -------------------------------------------------------------------------------- /man/S7_class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/class.R 3 | \name{S7_class} 4 | \alias{S7_class} 5 | \title{Retrieve the S7 class of an object} 6 | \usage{ 7 | S7_class(object) 8 | } 9 | \arguments{ 10 | \item{object}{The S7 object} 11 | } 12 | \value{ 13 | An \link[=new_class]{S7 class}. 14 | } 15 | \description{ 16 | Given an S7 object, find it's class. 17 | } 18 | \examples{ 19 | Foo <- new_class("Foo") 20 | S7_class(Foo()) 21 | } 22 | -------------------------------------------------------------------------------- /man/S7_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \name{S7_data} 4 | \alias{S7_data} 5 | \alias{S7_data<-} 6 | \title{Get/set underlying "base" data} 7 | \usage{ 8 | S7_data(object) 9 | 10 | S7_data(object, check = TRUE) <- value 11 | } 12 | \arguments{ 13 | \item{object}{An object from a S7 class} 14 | 15 | \item{check}{If \code{TRUE}, check that \code{value} is of the correct type and run 16 | \code{\link[=validate]{validate()}} on the object before returning.} 17 | 18 | \item{value}{Object used to replace the underlying data.} 19 | } 20 | \value{ 21 | \code{S7_data()} returns the data stored in the base object; 22 | \verb{S7_data<-()} is called for its side-effects and returns \code{object} 23 | invisibly. 24 | } 25 | \description{ 26 | When an S7 class inherits from an existing base type, it can be useful 27 | to work with the underlying object, i.e. the S7 object stripped of class 28 | and properties. 29 | } 30 | \examples{ 31 | Text <- new_class("Text", parent = class_character) 32 | y <- Text(c(foo = "bar")) 33 | y 34 | S7_data(y) 35 | 36 | S7_data(y) <- c("a", "b") 37 | y 38 | } 39 | -------------------------------------------------------------------------------- /man/S7_inherits.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/inherits.R 3 | \name{S7_inherits} 4 | \alias{S7_inherits} 5 | \alias{check_is_S7} 6 | \title{Does this object inherit from an S7 class?} 7 | \usage{ 8 | S7_inherits(x, class = NULL) 9 | 10 | check_is_S7(x, class = NULL, arg = deparse(substitute(x))) 11 | } 12 | \arguments{ 13 | \item{x}{An object} 14 | 15 | \item{class}{An S7 class or \code{NULL}. If \code{NULL}, tests whether \code{x} is an 16 | S7 object without testing for a specific class.} 17 | 18 | \item{arg}{Argument name used in error message.} 19 | } 20 | \value{ 21 | \itemize{ 22 | \item \code{S7_inherits()} returns a single \code{TRUE} or \code{FALSE}. 23 | \item \code{check_is_S7()} returns nothing; it's called for its side-effects. 24 | } 25 | } 26 | \description{ 27 | \itemize{ 28 | \item \code{S7_inherits()} returns \code{TRUE} or \code{FALSE}. 29 | \item \code{check_is_S7()} throws an error if \code{x} isn't the specified \code{class}. 30 | } 31 | } 32 | \note{ 33 | Starting with \R 4.3.0, \code{base::inherits()} can accept an S7 class as 34 | the second argument, supporting usage like \code{inherits(x, Foo)}. 35 | } 36 | \examples{ 37 | Foo1 <- new_class("Foo1") 38 | Foo2 <- new_class("Foo2") 39 | 40 | S7_inherits(Foo1(), Foo1) 41 | check_is_S7(Foo1()) 42 | check_is_S7(Foo1(), Foo1) 43 | 44 | S7_inherits(Foo1(), Foo2) 45 | try(check_is_S7(Foo1(), Foo2)) 46 | 47 | if (getRversion() >= "4.3.0") 48 | inherits(Foo1(), Foo1) 49 | } 50 | -------------------------------------------------------------------------------- /man/S7_object.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/zzz.R 3 | \name{S7_object} 4 | \alias{S7_object} 5 | \title{Base S7 class} 6 | \usage{ 7 | S7_object() 8 | } 9 | \value{ 10 | The base S7 object. 11 | } 12 | \description{ 13 | The base class from which all S7 classes eventually inherit from. 14 | } 15 | \examples{ 16 | 17 | S7_object 18 | } 19 | \keyword{internal} 20 | -------------------------------------------------------------------------------- /man/as_class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/class-spec.R 3 | \name{as_class} 4 | \alias{as_class} 5 | \title{Standard class specifications} 6 | \usage{ 7 | as_class(x, arg = deparse(substitute(x))) 8 | } 9 | \arguments{ 10 | \item{x}{A class specification. One of the following: 11 | \itemize{ 12 | \item An S7 class (created by \code{\link[=new_class]{new_class()}}). 13 | \item An S7 union (created by \code{\link[=new_union]{new_union()}}). 14 | \item An S3 class (created by \code{\link[=new_S3_class]{new_S3_class()}}). 15 | \item An S4 class (created by \code{\link[methods:getClass]{methods::getClass()}} or \code{\link[methods:new]{methods::new()}}). 16 | \item A base class, like \link{class_logical}, \link{class_integer}, or \link{class_double}. 17 | \item A "special", either \link{class_missing} or \link{class_any}. 18 | }} 19 | 20 | \item{arg}{Argument name used when generating errors.} 21 | } 22 | \value{ 23 | A standardised class: either \code{NULL}, an S7 class, an S7 union, 24 | as \link{new_S3_class}, or a S4 class. 25 | } 26 | \description{ 27 | This is used as the interface between S7 and R's other OO systems, allowing 28 | you to use S7 classes and methods with base types, informal S3 classes, and 29 | formal S4 classes. 30 | } 31 | \examples{ 32 | as_class(class_logical) 33 | as_class(new_S3_class("factor")) 34 | } 35 | \keyword{internal} 36 | -------------------------------------------------------------------------------- /man/base_classes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/base.R 3 | \docType{data} 4 | \name{base_classes} 5 | \alias{base_classes} 6 | \alias{class_logical} 7 | \alias{class_integer} 8 | \alias{class_double} 9 | \alias{class_complex} 10 | \alias{class_character} 11 | \alias{class_raw} 12 | \alias{class_list} 13 | \alias{class_expression} 14 | \alias{class_name} 15 | \alias{class_call} 16 | \alias{class_function} 17 | \alias{class_environment} 18 | \alias{class_numeric} 19 | \alias{class_atomic} 20 | \alias{class_vector} 21 | \alias{class_language} 22 | \title{S7 wrappers for base types} 23 | \usage{ 24 | class_logical 25 | 26 | class_integer 27 | 28 | class_double 29 | 30 | class_complex 31 | 32 | class_character 33 | 34 | class_raw 35 | 36 | class_list 37 | 38 | class_expression 39 | 40 | class_name 41 | 42 | class_call 43 | 44 | class_function 45 | 46 | class_environment 47 | 48 | class_numeric 49 | 50 | class_atomic 51 | 52 | class_vector 53 | 54 | class_language 55 | } 56 | \value{ 57 | S7 classes wrapping around common base types and S3 classes. 58 | } 59 | \description{ 60 | The following S7 classes represent base types allowing them to be used 61 | within S7: 62 | \itemize{ 63 | \item \code{class_logical} 64 | \item \code{class_integer} 65 | \item \code{class_double} 66 | \item \code{class_complex} 67 | \item \code{class_character} 68 | \item \code{class_raw} 69 | \item \code{class_list} 70 | \item \code{class_expression} 71 | \item \code{class_name} 72 | \item \code{class_call} 73 | \item \code{class_function} 74 | \item \code{class_environment} (can only be used for properties) 75 | } 76 | 77 | We also include three union types to model numerics, atomics, and vectors 78 | respectively: 79 | \itemize{ 80 | \item \code{class_numeric} is a union of \code{class_integer} and \code{class_double}. 81 | \item \code{class_atomic} is a union of \code{class_logical}, \code{class_numeric}, 82 | \code{class_complex}, \code{class_character}, and \code{class_raw}. 83 | \item \code{class_vector} is a union of \code{class_atomic}, \code{class_list}, and 84 | \code{class_expression}. 85 | \item \code{class_language} is a union of \code{class_name} and \code{class_call}. 86 | } 87 | } 88 | \examples{ 89 | 90 | class_integer 91 | class_numeric 92 | class_factor 93 | } 94 | \keyword{datasets} 95 | -------------------------------------------------------------------------------- /man/base_s3_classes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/S3.R 3 | \docType{data} 4 | \name{base_s3_classes} 5 | \alias{base_s3_classes} 6 | \alias{class_factor} 7 | \alias{class_Date} 8 | \alias{class_POSIXct} 9 | \alias{class_POSIXlt} 10 | \alias{class_POSIXt} 11 | \alias{class_data.frame} 12 | \alias{class_formula} 13 | \title{S7 wrappers for key S3 classes} 14 | \usage{ 15 | class_factor 16 | 17 | class_Date 18 | 19 | class_POSIXct 20 | 21 | class_POSIXlt 22 | 23 | class_POSIXt 24 | 25 | class_data.frame 26 | 27 | class_formula 28 | } 29 | \description{ 30 | S7 bundles \link[=new_S3_class]{S3 definitions} for key S3 classes provided by 31 | the base packages: 32 | \itemize{ 33 | \item \code{class_data.frame} for data frames. 34 | \item \code{class_Date} for dates. 35 | \item \code{class_factor} for factors. 36 | \item \code{class_POSIXct}, \code{class_POSIXlt} and \code{class_POSIXt} for date-times. 37 | \item \code{class_formula} for formulas. 38 | } 39 | } 40 | \keyword{datasets} 41 | -------------------------------------------------------------------------------- /man/class_any.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/special.R 3 | \docType{data} 4 | \name{class_any} 5 | \alias{class_any} 6 | \title{Dispatch on any class} 7 | \usage{ 8 | class_any 9 | } 10 | \description{ 11 | Use \code{class_any} to register a default method that is called when no other 12 | methods are matched. 13 | } 14 | \examples{ 15 | foo <- new_generic("foo", "x") 16 | method(foo, class_numeric) <- function(x) "number" 17 | method(foo, class_any) <- function(x) "fallback" 18 | 19 | foo(1) 20 | foo("x") 21 | } 22 | \keyword{datasets} 23 | -------------------------------------------------------------------------------- /man/class_missing.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/special.R 3 | \docType{data} 4 | \name{class_missing} 5 | \alias{class_missing} 6 | \title{Dispatch on a missing argument} 7 | \usage{ 8 | class_missing 9 | } 10 | \value{ 11 | Sentinel objects used for special types of dispatch. 12 | } 13 | \description{ 14 | Use \code{class_missing} to dispatch when the user has not supplied an argument, 15 | i.e. it's missing in the sense of \code{\link[=missing]{missing()}}, not in the sense of 16 | \code{\link[=is.na]{is.na()}}. 17 | } 18 | \examples{ 19 | foo <- new_generic("foo", "x") 20 | method(foo, class_numeric) <- function(x) "number" 21 | method(foo, class_missing) <- function(x) "missing" 22 | method(foo, class_any) <- function(x) "fallback" 23 | 24 | foo(1) 25 | foo() 26 | foo("") 27 | } 28 | \keyword{datasets} 29 | -------------------------------------------------------------------------------- /man/convert.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/convert.R 3 | \name{convert} 4 | \alias{convert} 5 | \title{Convert an object from one type to another} 6 | \usage{ 7 | convert(from, to, ...) 8 | } 9 | \arguments{ 10 | \item{from}{An S7 object to convert.} 11 | 12 | \item{to}{An S7 class specification, passed to \code{\link[=as_class]{as_class()}}.} 13 | 14 | \item{...}{Other arguments passed to custom \code{convert()} methods. For upcasting, 15 | these can be used to override existing properties or set new ones.} 16 | } 17 | \value{ 18 | Either \code{from} coerced to class \code{to}, or an error if the coercion 19 | is not possible. 20 | } 21 | \description{ 22 | \code{convert(from, to)} is a built-in generic for converting an object from 23 | one type to another. It is special in three ways: 24 | \itemize{ 25 | \item It uses double-dispatch, because conversion depends on both \code{from} and 26 | \code{to}. 27 | \item It uses non-standard dispatch because \code{to} is a class, not an object. 28 | \item It doesn't use inheritance for the \code{to} argument. To understand 29 | why, imagine you have written methods to objects of various types to 30 | \code{classParent}. If you then create a new \code{classChild} that inherits from 31 | \code{classParent}, you can't expect the methods written for \code{classParent} 32 | to work because those methods will return \code{classParent} objects, not 33 | \code{classChild} objects. 34 | } 35 | 36 | \code{convert()} provides two default implementations: 37 | \enumerate{ 38 | \item When \code{from} inherits from \code{to}, it strips any properties that \code{from} 39 | possesses that \code{to} does not (downcasting). 40 | \item When \code{to} is a subclass of \code{from}'s class, it creates a new object of 41 | class \code{to}, copying over existing properties from \code{from} and initializing 42 | new properties of \code{to} (upcasting). 43 | } 44 | 45 | If you are converting an object solely for the purposes of accessing a method 46 | on a superclass, you probably want \code{\link[=super]{super()}} instead. See its docs for more 47 | details. 48 | \subsection{S3 & S4}{ 49 | 50 | \code{convert()} plays a similar role to the convention of defining \code{as.foo()} 51 | functions/generics in S3, and to \code{as()}/\code{setAs()} in S4. 52 | } 53 | } 54 | \examples{ 55 | Foo1 <- new_class("Foo1", properties = list(x = class_integer)) 56 | Foo2 <- new_class("Foo2", Foo1, properties = list(y = class_double)) 57 | 58 | # Downcasting: S7 provides a default implementation for coercing an object 59 | # to one of its parent classes: 60 | convert(Foo2(x = 1L, y = 2), to = Foo1) 61 | 62 | # Upcasting: S7 also provides a default implementation for coercing an object 63 | # to one of its child classes: 64 | convert(Foo1(x = 1L), to = Foo2) 65 | convert(Foo1(x = 1L), to = Foo2, y = 2.5) # Set new property 66 | convert(Foo1(x = 1L), to = Foo2, x = 2L, y = 2.5) # Override existing and set new 67 | 68 | # For all other cases, you'll need to provide your own. 69 | try(convert(Foo1(x = 1L), to = class_integer)) 70 | 71 | method(convert, list(Foo1, class_integer)) <- function(from, to) { 72 | from@x 73 | } 74 | convert(Foo1(x = 1L), to = class_integer) 75 | 76 | # Note that conversion does not respect inheritance so if we define a 77 | # convert method for integer to foo1 78 | method(convert, list(class_integer, Foo1)) <- function(from, to) { 79 | Foo1(x = from) 80 | } 81 | convert(1L, to = Foo1) 82 | 83 | # Converting to Foo2 will still error 84 | try(convert(1L, to = Foo2)) 85 | # This is probably not surprising because foo2 also needs some value 86 | # for `@y`, but it definitely makes dispatch for convert() special 87 | } 88 | -------------------------------------------------------------------------------- /man/method-set.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/method-register.R 3 | \name{method<-} 4 | \alias{method<-} 5 | \title{Register an S7 method for a generic} 6 | \usage{ 7 | method(generic, signature) <- value 8 | } 9 | \arguments{ 10 | \item{generic}{A generic function, i.e. an \link[=new_generic]{S7 generic}, 11 | an \link[=new_external_generic]{external generic}, an \link[=UseMethod]{S3 generic}, 12 | or an \link[methods:setGeneric]{S4 generic}.} 13 | 14 | \item{signature}{A method signature. 15 | 16 | For S7 generics that use single dispatch, this must be one of the 17 | following: 18 | \itemize{ 19 | \item An S7 class (created by \code{\link[=new_class]{new_class()}}). 20 | \item An S7 union (created by \code{\link[=new_union]{new_union()}}). 21 | \item An S3 class (created by \code{\link[=new_S3_class]{new_S3_class()}}). 22 | \item An S4 class (created by \code{\link[methods:getClass]{methods::getClass()}} or \code{\link[methods:new]{methods::new()}}). 23 | \item A base type like \link{class_logical}, \link{class_integer}, or \link{class_numeric}. 24 | \item A special type like \link{class_missing} or \link{class_any}. 25 | } 26 | 27 | For S7 generics that use multiple dispatch, this must be a list of any of 28 | the above types. 29 | 30 | For S3 generics, this must be a single S7 class. 31 | 32 | For S4 generics, this must either be an S7 class, or a list that includes 33 | at least one S7 class.} 34 | 35 | \item{value}{A function that implements the generic specification for the 36 | given \code{signature}.} 37 | } 38 | \value{ 39 | The \code{generic}, invisibly. 40 | } 41 | \description{ 42 | A generic defines the interface of a function. Once you have created a 43 | generic with \code{\link[=new_generic]{new_generic()}}, you provide implementations for specific 44 | signatures by registering methods with \verb{method<-}. 45 | 46 | The goal is for \verb{method<-} to be the single function you need when working 47 | with S7 generics or S7 classes. This means that as well as registering 48 | methods for S7 classes on S7 generics, you can also register methods for 49 | S7 classes on S3 or S4 generics, and S3 or S4 classes on S7 generics. 50 | But this is not a general method registration function: at least one of 51 | \code{generic} and \code{signature} needs to be from S7. 52 | 53 | Note that if you are writing a package, you must call \code{\link[=methods_register]{methods_register()}} 54 | in your \code{.onLoad}. This ensures that all methods are dynamically registered 55 | when needed. 56 | } 57 | \examples{ 58 | # Create a generic 59 | bizarro <- new_generic("bizarro", "x") 60 | # Register some methods 61 | method(bizarro, class_numeric) <- function(x) rev(x) 62 | method(bizarro, new_S3_class("data.frame")) <- function(x) { 63 | x[] <- lapply(x, bizarro) 64 | rev(x) 65 | } 66 | 67 | # Using a generic calls the methods automatically 68 | bizarro(head(mtcars)) 69 | } 70 | -------------------------------------------------------------------------------- /man/method.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/method-introspect.R 3 | \name{method} 4 | \alias{method} 5 | \title{Find a method for an S7 generic} 6 | \usage{ 7 | method(generic, class = NULL, object = NULL) 8 | } 9 | \arguments{ 10 | \item{generic}{A generic function, i.e. an \link[=new_generic]{S7 generic}, 11 | an \link[=new_external_generic]{external generic}, an \link[=UseMethod]{S3 generic}, 12 | or an \link[methods:setGeneric]{S4 generic}.} 13 | 14 | \item{class, object}{Perform introspection either with a \code{class} 15 | (processed with \code{\link[=as_class]{as_class()}}) or a concrete \code{object}. If \code{generic} uses 16 | multiple dispatch then both \code{object} and \code{class} must be a list of 17 | classes/objects.} 18 | } 19 | \value{ 20 | Either a function with class \code{S7_method} or an error if no 21 | matching method is found. 22 | } 23 | \description{ 24 | \code{method()} takes a generic and class signature and performs method dispatch 25 | to find the corresponding method implementation. This is rarely needed 26 | because you'll usually rely on the the generic to do dispatch for you (via 27 | \code{\link[=S7_dispatch]{S7_dispatch()}}). However, this introspection is useful if you want to see 28 | the implementation of a specific method. 29 | } 30 | \examples{ 31 | # Create a generic and register some methods 32 | bizarro <- new_generic("bizarro", "x") 33 | method(bizarro, class_numeric) <- function(x) rev(x) 34 | method(bizarro, class_factor) <- function(x) { 35 | levels(x) <- rev(levels(x)) 36 | x 37 | } 38 | 39 | # Printing the generic shows the registered method 40 | bizarro 41 | 42 | # And you can use method() to inspect specific implementations 43 | method(bizarro, class = class_integer) 44 | method(bizarro, object = 1) 45 | method(bizarro, class = class_factor) 46 | 47 | # errors if method not found 48 | try(method(bizarro, class = class_data.frame)) 49 | try(method(bizarro, object = "x")) 50 | } 51 | \seealso{ 52 | \code{\link[=method_explain]{method_explain()}} to explain why a specific method was picked. 53 | } 54 | -------------------------------------------------------------------------------- /man/method_explain.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/method-introspect.R 3 | \name{method_explain} 4 | \alias{method_explain} 5 | \title{Explain method dispatch} 6 | \usage{ 7 | method_explain(generic, class = NULL, object = NULL) 8 | } 9 | \arguments{ 10 | \item{generic}{A generic function, i.e. an \link[=new_generic]{S7 generic}, 11 | an \link[=new_external_generic]{external generic}, an \link[=UseMethod]{S3 generic}, 12 | or an \link[methods:setGeneric]{S4 generic}.} 13 | 14 | \item{class, object}{Perform introspection either with a \code{class} 15 | (processed with \code{\link[=as_class]{as_class()}}) or a concrete \code{object}. If \code{generic} uses 16 | multiple dispatch then both \code{object} and \code{class} must be a list of 17 | classes/objects.} 18 | } 19 | \value{ 20 | Nothing; this function is called for it's side effects. 21 | } 22 | \description{ 23 | \code{method_explain()} shows all possible methods that a call to a generic 24 | might use, which ones exist, and which one will actually be called. 25 | 26 | Note that method dispatch uses a string representation of each class in 27 | the class hierarchy. Each class system uses a slightly different convention 28 | to avoid ambiguity. 29 | \itemize{ 30 | \item S7: \code{pkg::class} or \code{class} 31 | \item S4: \code{S4/pkg::class} or \code{S4/class} 32 | \item S3: \code{class} 33 | } 34 | } 35 | \examples{ 36 | Foo1 <- new_class("Foo1") 37 | Foo2 <- new_class("Foo2", Foo1) 38 | 39 | add <- new_generic("add", c("x", "y")) 40 | method(add, list(Foo2, Foo1)) <- function(x, y) c(2, 1) 41 | method(add, list(Foo1, Foo1)) <- function(x, y) c(1, 1) 42 | 43 | method_explain(add, list(Foo2, Foo2)) 44 | } 45 | -------------------------------------------------------------------------------- /man/methods_register.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/external-generic.R 3 | \name{methods_register} 4 | \alias{methods_register} 5 | \title{Register methods in a package} 6 | \usage{ 7 | methods_register() 8 | } 9 | \value{ 10 | Nothing; called for its side-effects. 11 | } 12 | \description{ 13 | When using S7 in a package you should always call \code{methods_register()} when 14 | your package is loaded. This ensures that methods are registered as needed 15 | when you implement methods for generics (S3, S4, and S7) in other packages. 16 | (This is not strictly necessary if you only register methods for generics 17 | in your package, but it's better to include it and not need it than forget 18 | to include it and hit weird errors.) 19 | } 20 | \examples{ 21 | .onLoad <- function(...) { 22 | S7::methods_register() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /man/new_S3_class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/S3.R 3 | \name{new_S3_class} 4 | \alias{new_S3_class} 5 | \title{Declare an S3 class} 6 | \usage{ 7 | new_S3_class(class, constructor = NULL, validator = NULL) 8 | } 9 | \arguments{ 10 | \item{class}{S3 class vector (i.e. what \code{class()} returns). For method 11 | registration, you can abbreviate this to a single string, the S3 class 12 | name.} 13 | 14 | \item{constructor}{An optional constructor that can be used to create 15 | objects of the specified class. This is only needed if you wish to 16 | have an S7 class inherit from an S3 class or to use the S3 class as a 17 | property without a default. It must be specified in the 18 | same way as a S7 constructor: the first argument should be \code{.data} 19 | (the base type whose attributes will be modified). 20 | 21 | All arguments to the constructor should have default values so that 22 | when the constructor is called with no arguments, it returns returns 23 | an "empty", but valid, object.} 24 | 25 | \item{validator}{An optional validator used by \code{\link[=validate]{validate()}} to check that 26 | the S7 object adheres to the constraints of the S3 class. 27 | 28 | A validator is a single argument function that takes the object to 29 | validate and returns \code{NULL} if the object is valid. If the object is 30 | invalid, it returns a character vector of problems.} 31 | } 32 | \value{ 33 | An S7 definition of an S3 class, i.e. a list with class 34 | \code{S7_S3_class}. 35 | } 36 | \description{ 37 | To use an S3 class with S7, you must explicitly declare it using 38 | \code{new_S3_class()} because S3 lacks a formal class definition. 39 | (Unless it's an important base class already defined in \link{base_s3_classes}.) 40 | } 41 | \section{Method dispatch, properties, and unions}{ 42 | There are three ways of using S3 with S7 that only require the S3 class 43 | vector: 44 | \itemize{ 45 | \item Registering a S3 method for an S7 generic. 46 | \item Restricting an S7 property to an S3 class. 47 | \item Using an S3 class in an S7 union. 48 | } 49 | 50 | This is easy, and you can usually include the \code{new_S3_class()} 51 | call inline: 52 | 53 | \if{html}{\out{
}}\preformatted{method(my_generic, new_S3_class("factor")) <- function(x) "A factor" 54 | new_class("MyClass", properties = list(types = new_S3_class("factor"))) 55 | new_union("character", new_S3_class("factor")) 56 | }\if{html}{\out{
}} 57 | } 58 | 59 | \section{Extending an S3 class}{ 60 | Creating an S7 class that extends an S3 class requires more work. You'll 61 | also need to provide a constructor for the S3 class that follows S7 62 | conventions. This means the first argument to the constructor should be 63 | \code{.data}, and it should be followed by one argument for each attribute used 64 | by the class. 65 | 66 | This can be awkward because base S3 classes are usually heavily wrapped for user 67 | convenience and no low level constructor is available. For example, the 68 | factor class is an integer vector with a character vector of \code{levels}, but 69 | there's no base R function that takes an integer vector of values and 70 | character vector of levels, verifies that they are consistent, then 71 | creates a factor object. 72 | 73 | You may optionally want to also provide a \code{validator} function which will 74 | ensure that \code{\link[=validate]{validate()}} confirms the validity of any S7 classes that build 75 | on this class. Unlike an S7 validator, you are responsible for validating 76 | the types of the attributes. 77 | 78 | The following code shows how you might wrap the base Date class. 79 | A Date is a numeric vector with class \code{Date} that can be constructed with 80 | \code{.Date()}. 81 | 82 | \if{html}{\out{
}}\preformatted{S3_Date <- new_S3_class("Date", 83 | function(.data = integer()) \{ 84 | .Date(.data) 85 | \}, 86 | function(self) \{ 87 | if (!is.numeric(self)) \{ 88 | "Underlying data must be numeric" 89 | \} 90 | \} 91 | ) 92 | }\if{html}{\out{
}} 93 | } 94 | 95 | \examples{ 96 | # No checking, just used for dispatch 97 | Date <- new_S3_class("Date") 98 | 99 | my_generic <- new_generic("my_generic", "x") 100 | method(my_generic, Date) <- function(x) "This is a date" 101 | 102 | my_generic(Sys.Date()) 103 | } 104 | -------------------------------------------------------------------------------- /man/new_external_generic.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/external-generic.R 3 | \name{new_external_generic} 4 | \alias{new_external_generic} 5 | \title{Generics in other packages} 6 | \usage{ 7 | new_external_generic(package, name, dispatch_args, version = NULL) 8 | } 9 | \arguments{ 10 | \item{package}{Package the generic is defined in.} 11 | 12 | \item{name}{Name of generic, as a string.} 13 | 14 | \item{dispatch_args}{Character vector giving arguments used for dispatch.} 15 | 16 | \item{version}{An optional version the package must meet for the method to 17 | be registered.} 18 | } 19 | \value{ 20 | An S7 external generic, i.e. a list with class 21 | \code{S7_external_generic}. 22 | } 23 | \description{ 24 | You need an explicit external generic when you want to provide methods 25 | for a generic (S3, S4, or S7) that is defined in another package, and you 26 | don't want to take a hard dependency on that package. 27 | 28 | The easiest way to provide methods for generics in other packages is 29 | import the generic into your \code{NAMESPACE}. This, however, creates a hard 30 | dependency, and sometimes you want a soft dependency, only registering the 31 | method if the package is already installed. \code{new_external_generic()} allows 32 | you to provide the minimal needed information about a generic so that methods 33 | can be registered at run time, as needed, using \code{\link[=methods_register]{methods_register()}}. 34 | 35 | Note that in tests, you'll need to explicitly call the generic from the 36 | external package with \code{pkg::generic()}. 37 | } 38 | \examples{ 39 | MyClass <- new_class("MyClass") 40 | 41 | your_generic <- new_external_generic("stats", "median", "x") 42 | method(your_generic, MyClass) <- function(x) "Hi!" 43 | } 44 | -------------------------------------------------------------------------------- /man/new_generic.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/generic.R, R/method-dispatch.R 3 | \name{new_generic} 4 | \alias{new_generic} 5 | \alias{S7_dispatch} 6 | \title{Define a new generic} 7 | \usage{ 8 | new_generic(name, dispatch_args, fun = NULL) 9 | 10 | S7_dispatch() 11 | } 12 | \arguments{ 13 | \item{name}{The name of the generic. This should be the same as the object 14 | that you assign it to.} 15 | 16 | \item{dispatch_args}{A character vector giving the names of one or more 17 | arguments used to find the method.} 18 | 19 | \item{fun}{An optional specification of the generic, which must call 20 | \code{S7_dispatch()} to dispatch to methods. This is usually generated 21 | automatically from the \code{dispatch_args}, but you may want to supply it if 22 | you want to add additional required arguments, omit \code{...}, or perform 23 | some standardised computation in the generic. 24 | 25 | The \code{dispatch_args} must be the first arguments to \code{fun}, and, if present, 26 | \code{...} must immediately follow them.} 27 | } 28 | \value{ 29 | An S7 generic, i.e. a function with class \code{S7_generic}. 30 | } 31 | \description{ 32 | A generic function uses different implementations (\emph{methods}) depending on 33 | the class of one or more arguments (the \emph{signature}). Create a new generic 34 | with \code{new_generic()} then use \link{method<-} to add methods to it. 35 | 36 | Method dispatch is performed by \code{S7_dispatch()}, which must always be 37 | included in the body of the generic, but in most cases \code{new_generic()} will 38 | generate this for you. 39 | 40 | Learn more in \code{vignette("generics-methods")} 41 | } 42 | \section{Dispatch arguments}{ 43 | 44 | The arguments that are used to pick the method are called the \strong{dispatch 45 | arguments}. In most cases, this will be one argument, in which case the 46 | generic is said to use \strong{single dispatch}. If it consists of more than 47 | one argument, it's said to use \strong{multiple dispatch}. 48 | 49 | There are two restrictions on the dispatch arguments: they must be the first 50 | arguments to the generic and if the generic uses \code{...}, it must occur 51 | immediately after the dispatch arguments. 52 | } 53 | 54 | \examples{ 55 | # A simple generic with methods for some base types and S3 classes 56 | type_of <- new_generic("type_of", dispatch_args = "x") 57 | method(type_of, class_character) <- function(x, ...) "A character vector" 58 | method(type_of, new_S3_class("data.frame")) <- function(x, ...) "A data frame" 59 | method(type_of, class_function) <- function(x, ...) "A function" 60 | 61 | type_of(mtcars) 62 | type_of(letters) 63 | type_of(mean) 64 | 65 | # If you want to require that methods implement additional arguments, 66 | # you can use a custom function: 67 | mean2 <- new_generic("mean2", "x", function(x, ..., na.rm = FALSE) { 68 | S7_dispatch() 69 | }) 70 | 71 | method(mean2, class_numeric) <- function(x, ..., na.rm = FALSE) { 72 | if (na.rm) { 73 | x <- x[!is.na(x)] 74 | } 75 | sum(x) / length(x) 76 | } 77 | 78 | # You'll be warned if you forget the argument: 79 | method(mean2, class_character) <- function(x, ...) { 80 | stop("Not supported") 81 | } 82 | } 83 | \seealso{ 84 | \code{\link[=new_external_generic]{new_external_generic()}} to define a method for a generic 85 | in another package without taking a strong dependency on it. 86 | } 87 | -------------------------------------------------------------------------------- /man/new_property.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/property.R 3 | \name{new_property} 4 | \alias{new_property} 5 | \title{Define a new property} 6 | \usage{ 7 | new_property( 8 | class = class_any, 9 | getter = NULL, 10 | setter = NULL, 11 | validator = NULL, 12 | default = NULL, 13 | name = NULL 14 | ) 15 | } 16 | \arguments{ 17 | \item{class}{Class that the property must be an instance of. 18 | See \code{\link[=as_class]{as_class()}} for details.} 19 | 20 | \item{getter}{An optional function used to get the value. The function 21 | should take \code{self} as its sole argument and return the value. If you 22 | supply a \code{getter}, you are responsible for ensuring that it returns 23 | an object of the correct \code{class}; it will not be validated automatically. 24 | 25 | If a property has a getter but doesn't have a setter, it is read only.} 26 | 27 | \item{setter}{An optional function used to set the value. The function 28 | should take \code{self} and \code{value} and return a modified object.} 29 | 30 | \item{validator}{A function taking a single argument, \code{value}, the value 31 | to validate. 32 | 33 | The job of a validator is to determine whether the property value is valid. 34 | It should return \code{NULL} if the object is valid, or if it's not valid, 35 | a single string describing the problem. The message should not include the 36 | name of the property as this will be automatically appended to the 37 | beginning of the message. 38 | 39 | The validator will be called after the \code{class} has been verified, so 40 | your code can assume that \code{value} has known type.} 41 | 42 | \item{default}{When an object is created and the property is not supplied, 43 | what should it default to? If \code{NULL}, it defaults to the "empty" instance 44 | of \code{class}. This can also be a quoted call, which then becomes a standard 45 | function promise in the default constructor, evaluated at the time the 46 | object is constructed.} 47 | 48 | \item{name}{Property name, primarily used for error messages. Generally 49 | don't need to set this here, as it's more convenient to supply as 50 | the element name when defining a list of properties. If both \code{name} 51 | and a list-name are supplied, the list-name will be used.} 52 | } 53 | \value{ 54 | An S7 property, i.e. a list with class \code{S7_property}. 55 | } 56 | \description{ 57 | A property defines a named component of an object. Properties are 58 | typically used to store (meta) data about an object, and are often 59 | limited to a data of a specific \code{class}. 60 | 61 | By specifying a \code{getter} and/or \code{setter}, you can make the property 62 | "dynamic" so that it's computed when accessed or has some non-standard 63 | behaviour when modified. Dynamic properties are not included as an argument 64 | to the default class constructor. 65 | 66 | See the "Properties: Common Patterns" section in \code{vignette("class-objects")} 67 | for more examples. 68 | } 69 | \examples{ 70 | # Simple properties store data inside an object 71 | Pizza <- new_class("Pizza", properties = list( 72 | slices = new_property(class_numeric, default = 10) 73 | )) 74 | my_pizza <- Pizza(slices = 6) 75 | my_pizza@slices 76 | my_pizza@slices <- 5 77 | my_pizza@slices 78 | 79 | your_pizza <- Pizza() 80 | your_pizza@slices 81 | 82 | # Dynamic properties can compute on demand 83 | Clock <- new_class("Clock", properties = list( 84 | now = new_property(getter = function(self) Sys.time()) 85 | )) 86 | my_clock <- Clock() 87 | my_clock@now; Sys.sleep(1) 88 | my_clock@now 89 | # This property is read only, because there is a 'getter' but not a 'setter' 90 | try(my_clock@now <- 10) 91 | 92 | # Because the property is dynamic, it is not included as an 93 | # argument to the default constructor 94 | try(Clock(now = 10)) 95 | args(Clock) 96 | } 97 | -------------------------------------------------------------------------------- /man/new_union.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/union.R 3 | \name{new_union} 4 | \alias{new_union} 5 | \title{Define a class union} 6 | \usage{ 7 | new_union(...) 8 | } 9 | \arguments{ 10 | \item{...}{The classes to include in the union. See \code{\link[=as_class]{as_class()}} for 11 | details.} 12 | } 13 | \value{ 14 | An S7 union, i.e. a list with class \code{S7_union}. 15 | } 16 | \description{ 17 | A class union represents a list of possible classes. You can create it 18 | with \code{new_union(a, b, c)} or \code{a | b | c}. Unions can be used in two 19 | places: 20 | \itemize{ 21 | \item To allow a property to be one of a set of classes, 22 | \code{new_property(class_integer | Range)}. The default \code{default} value for the 23 | property will be the constructor of the first object in the union. 24 | This means if you want to create an "optional" property (i.e. one that 25 | can be \code{NULL} or of a specified type), you'll need to write (e.g.) 26 | \code{NULL | class_integer}. 27 | \item As a convenient short-hand to define methods for multiple classes. 28 | \code{method(foo, X | Y) <- f} is short-hand for 29 | \verb{method(foo, X) <- f; method(foo, Y) <- foo} 30 | } 31 | 32 | S7 includes built-in unions for "numeric" (integer and double vectors), 33 | "atomic" (logical, numeric, complex, character, and raw vectors) and 34 | "vector" (atomic vectors, lists, and expressions). 35 | } 36 | \examples{ 37 | logical_or_character <- new_union(class_logical, class_character) 38 | logical_or_character 39 | # or with shortcut syntax 40 | logical_or_character <- class_logical | class_character 41 | 42 | Foo <- new_class("Foo", properties = list(x = logical_or_character)) 43 | Foo(x = TRUE) 44 | Foo(x = letters[1:5]) 45 | try(Foo(1:3)) 46 | 47 | bar <- new_generic("bar", "x") 48 | # Use built-in union 49 | method(bar, class_atomic) <- function(x) "Hi!" 50 | bar 51 | bar(TRUE) 52 | bar(letters) 53 | try(bar(NULL)) 54 | } 55 | -------------------------------------------------------------------------------- /man/prop.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/compatibility.R, R/property.R 3 | \name{prop} 4 | \alias{prop} 5 | \alias{@} 6 | \alias{prop<-} 7 | \alias{@.S7_object} 8 | \title{Get/set a property} 9 | \usage{ 10 | prop(object, name) 11 | 12 | prop(object, name, check = TRUE) <- value 13 | 14 | object@name 15 | } 16 | \arguments{ 17 | \item{object}{An object from a S7 class} 18 | 19 | \item{name}{The name of the parameter as a character. Partial matching 20 | is not performed.} 21 | 22 | \item{check}{If \code{TRUE}, check that \code{value} is of the correct type and run 23 | \code{\link[=validate]{validate()}} on the object before returning.} 24 | 25 | \item{value}{A new value for the property. The object is automatically 26 | checked for validity after the replacement is done.} 27 | } 28 | \value{ 29 | \code{prop()} and \code{@} return the value of the property. 30 | \verb{prop<-()} and \verb{@<-} are called for their side-effects and return 31 | the modified object, invisibly. 32 | } 33 | \description{ 34 | \itemize{ 35 | \item \code{prop(x, "name")} / \code{prop@name} get the value of the a property, 36 | erroring if it the property doesn't exist. 37 | \item \code{prop(x, "name") <- value} / \code{prop@name <- value} set the value of 38 | a property. 39 | } 40 | } 41 | \examples{ 42 | Horse <- new_class("Horse", properties = list( 43 | name = class_character, 44 | colour = class_character, 45 | height = class_numeric 46 | )) 47 | lexington <- Horse(colour = "bay", height = 15, name = "Lex") 48 | lexington@colour 49 | prop(lexington, "colour") 50 | 51 | lexington@height <- 14 52 | prop(lexington, "height") <- 15 53 | } 54 | -------------------------------------------------------------------------------- /man/prop_names.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/property.R 3 | \name{prop_names} 4 | \alias{prop_names} 5 | \alias{prop_exists} 6 | \title{Property introspection} 7 | \usage{ 8 | prop_names(object) 9 | 10 | prop_exists(object, name) 11 | } 12 | \arguments{ 13 | \item{object}{An object from a S7 class} 14 | 15 | \item{name}{The name of the parameter as a character. Partial matching 16 | is not performed.} 17 | } 18 | \value{ 19 | \code{prop_names()} returns a character vector; \code{prop_exists()} returns 20 | a single \code{TRUE} or \code{FALSE}. 21 | } 22 | \description{ 23 | \itemize{ 24 | \item \code{prop_names(x)} returns the names of the properties 25 | \item \code{prop_exists(x, "prop")} returns \code{TRUE} iif \code{x} has property \code{prop}. 26 | } 27 | } 28 | \examples{ 29 | Foo <- new_class("Foo", properties = list(a = class_character, b = class_integer)) 30 | f <- Foo() 31 | 32 | prop_names(f) 33 | prop_exists(f, "a") 34 | prop_exists(f, "c") 35 | } 36 | -------------------------------------------------------------------------------- /man/props.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/property.R 3 | \name{props} 4 | \alias{props} 5 | \alias{props<-} 6 | \alias{set_props} 7 | \title{Get/set multiple properties} 8 | \usage{ 9 | props(object, names = prop_names(object)) 10 | 11 | props(object) <- value 12 | 13 | set_props(object, ...) 14 | } 15 | \arguments{ 16 | \item{object}{An object from a S7 class} 17 | 18 | \item{names}{A character vector of property names to retrieve. Default is all 19 | properties.} 20 | 21 | \item{value}{A named list of values. The object is checked for validity 22 | only after all replacements are performed.} 23 | 24 | \item{...}{Name-value pairs given property to modify and new value.} 25 | } 26 | \value{ 27 | A named list of property values. 28 | } 29 | \description{ 30 | \itemize{ 31 | \item \code{props(x)} returns all properties. 32 | \item \code{props(x) <- list(name1 = val1, name2 = val2)} modifies an existing object 33 | by setting multiple properties simultaneously. 34 | \item \code{set_props(x, name1 = val1, name2 = val2)} creates a copy of an existing 35 | object with new values for the specified properties. 36 | } 37 | } 38 | \examples{ 39 | Horse <- new_class("Horse", properties = list( 40 | name = class_character, 41 | colour = class_character, 42 | height = class_numeric 43 | )) 44 | lexington <- Horse(colour = "bay", height = 15, name = "Lex") 45 | 46 | props(lexington) 47 | props(lexington) <- list(height = 14, name = "Lexington") 48 | lexington 49 | } 50 | -------------------------------------------------------------------------------- /man/super.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/super.R 3 | \name{super} 4 | \alias{super} 5 | \title{Force method dispatch to use a superclass} 6 | \usage{ 7 | super(from, to) 8 | } 9 | \arguments{ 10 | \item{from}{An S7 object to cast.} 11 | 12 | \item{to}{An S7 class specification, passed to \code{\link[=as_class]{as_class()}}. Must be a 13 | superclass of \code{object}.} 14 | } 15 | \value{ 16 | An \code{S7_super} object which should always be passed 17 | immediately to a generic. It has no other special behavior. 18 | } 19 | \description{ 20 | \code{super(from, to)} causes the dispatch for the next generic to use the method 21 | for the superclass \code{to} instead of the actual class of \code{from}. It's needed 22 | when you want to implement a method in terms of the implementation of its 23 | superclass. 24 | \subsection{S3 & S4}{ 25 | 26 | \code{super()} performs a similar role to \code{\link[=NextMethod]{NextMethod()}} in S3 or 27 | \code{\link[methods:NextMethod]{methods::callNextMethod()}} in S4, but is much more explicit: 28 | \itemize{ 29 | \item The super class that \code{super()} will use is known when write \code{super()} 30 | (i.e. statically) as opposed to when the generic is called 31 | (i.e. dynamically). 32 | \item All arguments to the generic are explicit; they are not automatically 33 | passed along. 34 | } 35 | 36 | This makes \code{super()} more verbose, but substantially easier to 37 | understand and reason about. 38 | } 39 | 40 | \subsection{\code{super()} in S3 generics}{ 41 | 42 | Note that you can't use \code{super()} in methods for an S3 generic. 43 | For example, imagine that you have made a subclass of "integer": 44 | 45 | \if{html}{\out{
}}\preformatted{MyInt <- new_class("MyInt", parent = class_integer, package = NULL) 46 | }\if{html}{\out{
}} 47 | 48 | Now you go to write a custom print method: 49 | 50 | \if{html}{\out{
}}\preformatted{method(print, MyInt) <- function(x, ...) \{ 51 | cat("") 52 | print(super(x, to = class_integer)) 53 | \} 54 | 55 | MyInt(10L) 56 | #> super(, ) 57 | }\if{html}{\out{
}} 58 | 59 | This doesn't work because \code{print()} isn't an S7 generic so doesn't 60 | understand how to interpret the special object that \code{super()} produces. 61 | While you could resolve this problem with \code{\link[=NextMethod]{NextMethod()}} (because S7 is 62 | implemented on top of S3), we instead recommend using \code{\link[=S7_data]{S7_data()}} to extract 63 | the underlying base object: 64 | 65 | \if{html}{\out{
}}\preformatted{method(print, MyInt) <- function(x, ...) \{ 66 | cat("") 67 | print(S7_data(x)) 68 | \} 69 | 70 | MyInt(10L) 71 | #> [1] 10 72 | }\if{html}{\out{
}} 73 | } 74 | } 75 | \examples{ 76 | Foo1 <- new_class("Foo1", properties = list(x = class_numeric, y = class_numeric)) 77 | Foo2 <- new_class("Foo2", Foo1, properties = list(z = class_numeric)) 78 | 79 | total <- new_generic("total", "x") 80 | method(total, Foo1) <- function(x) x@x + x@y 81 | 82 | # This won't work because it'll be stuck in an infinite loop: 83 | method(total, Foo2) <- function(x) total(x) + x@z 84 | 85 | # We could write 86 | method(total, Foo2) <- function(x) x@x + x@y + x@z 87 | # but then we'd need to remember to update it if the implementation 88 | # for total() ever changed. 89 | 90 | # So instead we use `super()` to call the method for the parent class: 91 | method(total, Foo2) <- function(x) total(super(x, to = Foo1)) + x@z 92 | total(Foo2(1, 2, 3)) 93 | 94 | # To see the difference between convert() and super() we need a 95 | # method that calls another generic 96 | 97 | bar1 <- new_generic("bar1", "x") 98 | method(bar1, Foo1) <- function(x) 1 99 | method(bar1, Foo2) <- function(x) 2 100 | 101 | bar2 <- new_generic("bar2", "x") 102 | method(bar2, Foo1) <- function(x) c(1, bar1(x)) 103 | method(bar2, Foo2) <- function(x) c(2, bar1(x)) 104 | 105 | obj <- Foo2(1, 2, 3) 106 | bar2(obj) 107 | # convert() affects every generic: 108 | bar2(convert(obj, to = Foo1)) 109 | # super() only affects the _next_ call to a generic: 110 | bar2(super(obj, to = Foo1)) 111 | } 112 | -------------------------------------------------------------------------------- /man/validate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/valid.R 3 | \name{validate} 4 | \alias{validate} 5 | \alias{valid_eventually} 6 | \alias{valid_implicitly} 7 | \title{Validate an S7 object} 8 | \usage{ 9 | validate(object, recursive = TRUE, properties = TRUE) 10 | 11 | valid_eventually(object, fun) 12 | 13 | valid_implicitly(object, fun) 14 | } 15 | \arguments{ 16 | \item{object}{An S7 object} 17 | 18 | \item{recursive}{If \code{TRUE}, calls validator of parent classes recursively.} 19 | 20 | \item{properties}{If \code{TRUE}, the default, checks property types before 21 | executing the validator.} 22 | 23 | \item{fun}{A function to call on the object before validation.} 24 | } 25 | \value{ 26 | Either \code{object} invisibly if valid, otherwise an error. 27 | } 28 | \description{ 29 | \code{validate()} ensures that an S7 object is valid by calling the \code{validator} 30 | provided in \code{\link[=new_class]{new_class()}}. This is done automatically when constructing new 31 | objects and when modifying properties. 32 | 33 | \code{valid_eventually()} disables validation, modifies the object, then 34 | revalidates. This is useful when a sequence of operations would otherwise 35 | lead an object to be temporarily invalid, or when repeated property 36 | modification causes a performance bottleneck because the validator is 37 | relatively expensive. 38 | 39 | \code{valid_implicitly()} does the same but does not validate the object at the 40 | end. It should only be used rarely, and in performance critical code where 41 | you are certain a sequence of operations cannot produce an invalid object. 42 | } 43 | \examples{ 44 | # A range class might validate that the start is less than the end 45 | Range <- new_class("Range", 46 | properties = list(start = class_double, end = class_double), 47 | validator = function(self) { 48 | if (self@start >= self@end) "start must be smaller than end" 49 | } 50 | ) 51 | # You can't construct an invalid object: 52 | try(Range(1, 1)) 53 | 54 | # And you can't create an invalid object with @<- 55 | r <- Range(1, 2) 56 | try(r@end <- 1) 57 | 58 | # But what if you want to move a range to the right? 59 | rightwards <- function(r, x) { 60 | r@start <- r@start + x 61 | r@end <- r@end + x 62 | r 63 | } 64 | # This function doesn't work because it creates a temporarily invalid state 65 | try(rightwards(r, 10)) 66 | 67 | # This is the perfect use case for valid_eventually(): 68 | rightwards <- function(r, x) { 69 | valid_eventually(r, function(object) { 70 | object@start <- object@start + x 71 | object@end <- object@end + x 72 | object 73 | }) 74 | } 75 | rightwards(r, 10) 76 | 77 | # Alternatively, you can set multiple properties at once using props<-, 78 | # which validates once at the end 79 | rightwards <- function(r, x) { 80 | props(r) <- list(start = r@start + x, end = r@end + x) 81 | r 82 | } 83 | rightwards(r, 20) 84 | } 85 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Revdeps 2 | 3 | ## New problems (1) 4 | 5 | |package |version |error |warning |note | 6 | |:-------|:-------|:--------|:-------|:------| 7 | |[fr](problems.md#fr)|0.5.1 |1 __+2__ |__+1__ |__+1__ | 8 | 9 | -------------------------------------------------------------------------------- /revdep/cloud.noindex/b283e3a4-953b-4875-b3dc-ee76fe345f59/crumble.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RConsortium/S7/db5964265a88d114fb6de97361060979ad63b3d6/revdep/cloud.noindex/b283e3a4-953b-4875-b3dc-ee76fe345f59/crumble.tar.gz -------------------------------------------------------------------------------- /revdep/cloud.noindex/b283e3a4-953b-4875-b3dc-ee76fe345f59/crumble/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: crumble 2 | Type: Package 3 | Title: Flexible and General Mediation Analysis Using Riesz Representers 4 | Version: 0.1.0 5 | Authors@R: 6 | c(person(given = "Nicholas", 7 | family = "Williams", 8 | role = c("aut", "cre", "cph"), 9 | email = "ntwilliams.personal@gmail.com", 10 | comment = c(ORCID = "0000-0002-1378-4831")), 11 | person(given = "Richard", 12 | family = "Liu", 13 | email = "li.liu@nyulangone.org", 14 | role = c("ctb")), 15 | person(given = "Iván", 16 | family = "Díaz", 17 | email = "ivan.diaz@nyulangone.org", 18 | role = c("aut", "cph"), 19 | comment = c(ORCID = "0000-0001-9056-2047"))) 20 | Maintainer: Nicholas Williams 21 | Description: Implements a modern, unified estimation strategy for common 22 | mediation estimands (natural effects, organic effects, interventional effects, 23 | and recanting twins) in combination with modified treatment policies as 24 | described in Liu, Williams, Rudolph, and Díaz (2024) 25 | . Estimation makes use of recent advancements 26 | in Riesz-learning to estimate a set of required nuisance parameters with 27 | deep learning. The result is the capability to estimate mediation effects with 28 | binary, categorical, continuous, or multivariate exposures with 29 | high-dimensional mediators and mediator-outcome confounders using machine 30 | learning. 31 | License: GPL (>= 3) 32 | Encoding: UTF-8 33 | Depends: R (>= 4.0.0) 34 | RoxygenNote: 7.3.2 35 | Imports: checkmate, Matrix, origami, torch, Rsymphony, purrr, cli, S7, 36 | data.table, coro, generics, lmtp, mlr3superlearner, progressr 37 | Suggests: testthat (>= 3.0.0), truncnorm, mma 38 | Config/testthat/edition: 3 39 | NeedsCompilation: no 40 | Packaged: 2024-09-17 16:37:07 UTC; nicholaswilliams 41 | Author: Nicholas Williams [aut, cre, cph] 42 | (), 43 | Richard Liu [ctb], 44 | Iván Díaz [aut, cph] () 45 | Repository: CRAN 46 | Date/Publication: 2024-09-18 11:50:05 UTC 47 | -------------------------------------------------------------------------------- /revdep/cloud.noindex/b283e3a4-953b-4875-b3dc-ee76fe345f59/crumble/new/crumble.Rcheck/crumble-Ex.Rout: -------------------------------------------------------------------------------- 1 | 2 | R version 4.3.1 (2023-06-16) -- "Beagle Scouts" 3 | Copyright (C) 2023 The R Foundation for Statistical Computing 4 | Platform: x86_64-pc-linux-gnu (64-bit) 5 | 6 | R is free software and comes with ABSOLUTELY NO WARRANTY. 7 | You are welcome to redistribute it under certain conditions. 8 | Type 'license()' or 'licence()' for distribution details. 9 | 10 | R is a collaborative project with many contributors. 11 | Type 'contributors()' for more information and 12 | 'citation()' on how to cite R or R packages in publications. 13 | 14 | Type 'demo()' for some demos, 'help()' for on-line help, or 15 | 'help.start()' for an HTML browser interface to help. 16 | Type 'q()' to quit R. 17 | 18 | > pkgname <- "crumble" 19 | > source(file.path(R.home("share"), "R", "examples-header.R")) 20 | > options(warn = 1) 21 | > library('crumble') 22 | > 23 | > base::assign(".oldSearch", base::search(), pos = 'CheckExEnv') 24 | > base::assign(".old_wd", base::getwd(), pos = 'CheckExEnv') 25 | > cleanEx() 26 | > nameEx("crumble") 27 | > ### * crumble 28 | > 29 | > flush(stderr()); flush(stdout()) 30 | > 31 | > ### Name: crumble 32 | > ### Title: Flexible and general mediation analysis 33 | > ### Aliases: crumble 34 | > 35 | > ### ** Examples 36 | > 37 | > 38 | > 39 | > 40 | > cleanEx() 41 | > nameEx("crumble_control") 42 | > ### * crumble_control 43 | > 44 | > flush(stderr()); flush(stdout()) 45 | > 46 | > ### Name: crumble_control 47 | > ### Title: Crumble control parameters 48 | > ### Aliases: crumble_control 49 | > 50 | > ### ** Examples 51 | > 52 | > if (torch::torch_is_installed()) crumble_control(crossfit_folds = 5) 53 | > 54 | > 55 | > 56 | > cleanEx() 57 | > nameEx("sequential_module") 58 | > ### * sequential_module 59 | > 60 | > flush(stderr()); flush(stdout()) 61 | > 62 | > ### Name: sequential_module 63 | > ### Title: Sequential neural network module function factory 64 | > ### Aliases: sequential_module 65 | > 66 | > ### ** Examples 67 | > 68 | > if (torch::torch_is_installed()) sequential_module() 69 | > 70 | > 71 | > 72 | > cleanEx() 73 | > nameEx("tidy.crumble") 74 | > ### * tidy.crumble 75 | > 76 | > flush(stderr()); flush(stdout()) 77 | > 78 | > ### Name: tidy.crumble 79 | > ### Title: Tidy a(n) crumble object 80 | > ### Aliases: tidy.crumble 81 | > 82 | > ### ** Examples 83 | > 84 | > 85 | > 86 | > 87 | > ### *