Title: | Another Object Orientation System |
---|---|
Description: | Another implementation of object-orientation in R. It provides syntactic sugar for the S4 class system and two alternative new implementations. One is an experimental version built around S4 and the other one makes it more convenient to work with lists as objects. |
Authors: | Sebastian Warnholz [aut, cre] |
Maintainer: | Sebastian Warnholz <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.5.0 |
Built: | 2025-01-31 02:53:24 UTC |
Source: | https://github.com/wahani/aoos |
This generic function only exists to test that the rexygen2 parser work correctly. Just ignore it.
.genericTest(x, ...) ## S4 method for signature 'numeric' .genericTest(x, ..., methodParam = function() 1)
.genericTest(x, ...) ## S4 method for signature 'numeric' .genericTest(x, ..., methodParam = function() 1)
x |
Object |
... |
Object |
methodParam |
Object |
These are two wrappers around setGeneric
and setMethod
. A
relevant difference is that generics and methods are stored in the
environment in which %g%
and %m%
are called and not in the
top-environment. Furthermore both functions have side effects in that they
will call globalVariables
for the arguments and name of the
generic.
lhs %g% rhs lhs %m% rhs
lhs %g% rhs lhs %m% rhs
lhs |
see details |
rhs |
the body as an expression |
The Syntax for the left hand side:
[<valueClass>:]<genericName>(<argList>)
- valueClass
optional, is the class of the return value (see
setGeneric)
- genericName
the name of the generic function
- argList
are name = value
or name ~ type
expressions. Name-Value expressions are just like in a function definition.
Name-Type expressions are used to define the signature of a method (see
setMethod). See %type% and the examples how to work with
them.
# A new generic function and a method: numeric : generic(x) %g% standardGeneric("generic") generic(x ~ numeric) %m% x generic(1) # Polymorphic methods in an object: Object <- function() { numeric : generic(x) %g% standardGeneric("generic") generic(x ~ numeric) %m% x retList("Object") } Object()$generic(1) # Class Unions: ## This generic allows for return values of type numeric or character: 'numeric | character' : generic(x) %g% standardGeneric("generic") ## This method also allows for numeric or character as argument: generic(x ~ character | numeric) %m% x generic(1) generic("")
# A new generic function and a method: numeric : generic(x) %g% standardGeneric("generic") generic(x ~ numeric) %m% x generic(1) # Polymorphic methods in an object: Object <- function() { numeric : generic(x) %g% standardGeneric("generic") generic(x ~ numeric) %m% x retList("Object") } Object()$generic(1) # Class Unions: ## This generic allows for return values of type numeric or character: 'numeric | character' : generic(x) %g% standardGeneric("generic") ## This method also allows for numeric or character as argument: generic(x ~ character | numeric) %m% x generic(1) generic("")
This function can be used to define new S4-classes which are called Type.
They have an initialize method and in the introduced syntax init-method and
S4-class definition build a unit, hence a type. This simply captures a
typical setClass
then setMethod("initialize", ...)
pattern
where often some redundancy is introduced. The function has side effects due
to calling setClass
, setMethod
and assigning the constructor
function to the types name.
lhs %type% rhs
lhs %type% rhs
lhs |
an expression of the form:
|
rhs |
the body of the initialize method as expression. It will be called
with |
Name-Type
expressions are also used in %m%. Besides this you
can formulate type unions in type expressions or the inheritance structure.
This has a side effect in that setClassUnion is called. Whenever you
write a type you can replace the name by an expression of the form:
type1 | type2
. Outside the slots or argument list of a method these
expressions have to be quoted. In this example the following expression is
evaluated for you: setClassUnion("type1ORtype2", c("type1", "type2"))
.
# This will create an S4-class named 'Test' with two slots; x = "numeric" # and y = "list"; prototype: list(x = 1, y = list()); and an initialize # method where some checks are performed. Test(x = 1, y = list()) %type% { stopifnot(.Object@x > 0) .Object } # This will create an S4-class named 'Numeric' with a slot and some tests. numeric : Numeric(metaInfo = character()) %type% { stopifnot(length(.Object) > 0) stopifnot(all(.Object > 0)) .Object } # This will create an S4-class with slots, where the constructor function has # no defaults. All slots will allow for ANY type. Anything(x, y ~ ANY, z = NULL) %type% .Object ## Not run: Anything() # error because x and y are missing ## End(Not run) # Type Unions: 'character | numeric' : Either(either ~ character | numeric) %type% .Object Either("", 1)
# This will create an S4-class named 'Test' with two slots; x = "numeric" # and y = "list"; prototype: list(x = 1, y = list()); and an initialize # method where some checks are performed. Test(x = 1, y = list()) %type% { stopifnot(.Object@x > 0) .Object } # This will create an S4-class named 'Numeric' with a slot and some tests. numeric : Numeric(metaInfo = character()) %type% { stopifnot(length(.Object) > 0) stopifnot(all(.Object > 0)) .Object } # This will create an S4-class with slots, where the constructor function has # no defaults. All slots will allow for ANY type. Anything(x, y ~ ANY, z = NULL) %type% .Object ## Not run: Anything() # error because x and y are missing ## End(Not run) # Type Unions: 'character | numeric' : Either(either ~ character | numeric) %type% .Object Either("", 1)
This is a virtual class to be contained in other class definitions. It overrides the default accessor $
and is intended to be used with the aoos class system (defineClass
). Inherit from this class if you want to access public fields in the same way you access lists.
## S4 method for signature 'Accessor' x$name ## S4 replacement method for signature 'Accessor' x$name <- value
## S4 method for signature 'Accessor' x$name ## S4 replacement method for signature 'Accessor' x$name <- value
x |
object |
name |
member name |
value |
value to assign to. |
This is an environment with some methods. Every class defined by defineClass
will inherit from aoos. Summary will show a list of public and private members with approximated memory usage.
## S4 method for signature 'aoos' show(object) ## S4 method for signature 'aoos' x$name ## S4 replacement method for signature 'aoos' x$name <- value ## S3 method for class 'aoos' summary(object, ...) ## S4 method for signature 'aoos' as.environment(x)
## S4 method for signature 'aoos' show(object) ## S4 method for signature 'aoos' x$name ## S4 replacement method for signature 'aoos' x$name <- value ## S3 method for class 'aoos' summary(object, ...) ## S4 method for signature 'aoos' as.environment(x)
object |
object |
x |
object |
name |
member name |
value |
value to assign to. Will throw an error. |
... |
arguments passed to method (not used). |
This is a virtual class to be contained in other class definitions. It can be used to define binary operators, e.g. +
or -
, inside an aoos class definition (defineClass
).
At the moment you can define binary operators as methods by naming them as .<binaryOperator>
(see the example). This is implemented for the following operators: +, -, *, /, %%, ^, <, >, ==, >=, <=, &
.
Rational <- defineClass("Rational", contains = c("Show", "Binary"), { numer <- 0 denom <- 1 .g <- 1 .gcd <- function(a, b) if(b == 0) a else Recall(b, a %% b) init <- function(numer, denom) { .self$.g <- .gcd(numer, denom) .self$numer <- numer / .g .self$denom <- denom / .g } show <- function() { cat(paste0(.self$numer, "/", .self$denom, "\n")) } ".+" <- function(that) { Rational(numer = numer * that$denom + that$numer * denom, denom = denom * that$denom) } neg <- function() { Rational(numer = -.self$numer, denom = .self$denom) } ".-" <- function(that) { .self + that$neg() } }) rational <- Rational(2, 3) rational + rational rational$neg() rational - rational
Rational <- defineClass("Rational", contains = c("Show", "Binary"), { numer <- 0 denom <- 1 .g <- 1 .gcd <- function(a, b) if(b == 0) a else Recall(b, a %% b) init <- function(numer, denom) { .self$.g <- .gcd(numer, denom) .self$numer <- numer / .g .self$denom <- denom / .g } show <- function() { cat(paste0(.self$numer, "/", .self$denom, "\n")) } ".+" <- function(that) { Rational(numer = numer * that$denom + that$numer * denom, denom = denom * that$denom) } neg <- function() { Rational(numer = -.self$numer, denom = .self$denom) } ".-" <- function(that) { .self + that$neg() } }) rational <- Rational(2, 3) rational + rational rational$neg() rational - rational
This is an experimental implementation of reference classes. Use defineRefClass
or retList
instead. defineClass
has side effects. The constructor is the return value of defineClass
.
defineClass(name, expr, contains = NULL) private(x) ## S4 method for signature 'public' private(x) public(x = NULL, validity = function(x) TRUE) ## S4 method for signature ''function'' public(x = NULL, validity = function(x) TRUE) ## S4 method for signature 'private' public(x = NULL, validity = function(x) TRUE) ## S4 method for signature 'public' public(x = NULL, validity = function(x) TRUE)
defineClass(name, expr, contains = NULL) private(x) ## S4 method for signature 'public' private(x) public(x = NULL, validity = function(x) TRUE) ## S4 method for signature ''function'' public(x = NULL, validity = function(x) TRUE) ## S4 method for signature 'private' public(x = NULL, validity = function(x) TRUE) ## S4 method for signature 'public' public(x = NULL, validity = function(x) TRUE)
name |
character name of the class |
expr |
expression |
contains |
character name of class from which to inherit |
x |
an object made public |
validity |
function to check the validity of an object |
defineClass
creates a S4-Class which can be used for standard S4 method dispatch. It will also set the method 'initialize' which need not to be changed. If you want to have some operations carried out on initialization use a function definition named init
as part of expr
. The return value from defineClass
is the constructor function. It has the argument ...
which will be passed to init
.
All classes defined with defineClass
inherit from class "aoos" which is a S4-class containing an environment. In that environment expr
is evaluated; for inheritance, all expr
from all parents will be evaluated first.
Everything in expr
will be part of the new class definition. A leading dot in a name will be interpreted as private. You can use public
and private
to declare private and public members explicitly. If x
in a call to public
is a function it will be a public member function (method). For any other class the return value of public
is a get and set method. If called without argument it will get the value, if called with argument it will set the value. You can define a validity function which will be called whenever the set method is called. Objects which inherit from class environment
can be accessed directly, i.e. not via get/set methods. If you want to access fields without get/set methods, you can use the class Accessor-class
.
Accessor-class
, Binary-class
, Show-class
test <- defineClass("test", { x <- "Working ..." .y <- 0 doSomething <- public(function() { self$.y <- .y + 1 cat(x(), "\n") invisible(self) }) }) instance <- test() ## Not run: instance$.y # error ## End(Not run) instance$doSomething()$doSomething() instance$x() instance$x(2) instance$x() # Example for reference classes as field MoreTesting <- defineClass("MoreTesting", { refObj <- test() }) instance <- MoreTesting() instance$refObj$x()
test <- defineClass("test", { x <- "Working ..." .y <- 0 doSomething <- public(function() { self$.y <- .y + 1 cat(x(), "\n") invisible(self) }) }) instance <- test() ## Not run: instance$.y # error ## End(Not run) instance$doSomething()$doSomething() instance$x() instance$x(2) instance$x() # Example for reference classes as field MoreTesting <- defineClass("MoreTesting", { refObj <- test() }) instance <- MoreTesting() instance$refObj$x()
This is a wrapper around setRefClass
. All arguments are defined in an expression (instead of lists) which improves readability of the code. Besides that, no additional features are added.
defineRefClass(expr)
defineRefClass(expr)
expr |
an expression |
## Not run: vignette("Introduction", "aoos") ## End(Not run) # Minimal example: Test <- defineRefClass({ Class <- "Test" # this is passed as argument to setRefClass x <- "character" # all objects which are not functions are fields do <- function() cat("Yes, Yes, I'm working...") # a method }) test <- Test() test$x <- "a" test$do() # Inheritance and privacy: pTest <- defineRefClass({ Class <- "pTest" # Privacy is solved by inheriting from a class 'Private' which redefines # the methods for access. contains <- c("Test", "Private") # passed as argument to setRefClass .y <- "numeric" # this is going to be 'private' doSomething <- function() { .self$.y <- 42 cat(x, .y, "\n") invisible(.self) } }) instance <- pTest() instance$x <- "Value of .y:" instance$doSomething() # A notion of privacy: stopifnot(inherits(try(instance$.y), "try-error")) stopifnot(inherits(try(instance$.y <- 2), "try-error"))
## Not run: vignette("Introduction", "aoos") ## End(Not run) # Minimal example: Test <- defineRefClass({ Class <- "Test" # this is passed as argument to setRefClass x <- "character" # all objects which are not functions are fields do <- function() cat("Yes, Yes, I'm working...") # a method }) test <- Test() test$x <- "a" test$do() # Inheritance and privacy: pTest <- defineRefClass({ Class <- "pTest" # Privacy is solved by inheriting from a class 'Private' which redefines # the methods for access. contains <- c("Test", "Private") # passed as argument to setRefClass .y <- "numeric" # this is going to be 'private' doSomething <- function() { .self$.y <- 42 cat(x, .y, "\n") invisible(.self) } }) instance <- pTest() instance$x <- "Value of .y:" instance$doSomething() # A notion of privacy: stopifnot(inherits(try(instance$.y), "try-error")) stopifnot(inherits(try(instance$.y <- 2), "try-error"))
Functions to help working with environments.
envCopy(from, to) envMerge(x, with)
envCopy(from, to) envMerge(x, with)
from |
environment |
to |
environment |
x |
environment |
with |
environment |
envCopy
tries to copy all objects in a given environment into the environment 'to'. Returns the names of copied objects.
envMerge
will merge x and with. Merge will copy all objects from x to with. Prior to that, the environment of functions are changed to be with iff functions in x have environment x; else the environment of functions are preserved.
retList where these are relevant.
These functions are used by roxygen2 for generating documentation.
"parser_%m%"(call, env, block) "parser_%g%"(call, env, block) "parser_%type%"(call, env, block)
"parser_%m%"(call, env, block) "parser_%g%"(call, env, block) "parser_%type%"(call, env, block)
call |
a call |
env |
an environment |
block |
is ignored |
There is no formal class definition for S3. Simply add 'Infix' or 'Print' to the class attribute and it inherits the methods. It is the same as Binary-class
or Show-class
just for S3. This is inteded to be used with retList
.
## S3 method for class 'Print' print(x, ...) ## S3 method for class 'Infix' e1 + e2 ## S3 method for class 'Infix' e1 - e2 ## S3 method for class 'Infix' e1 / e2 ## S3 method for class 'Infix' e1 %% e2 ## S3 method for class 'Infix' e1 ^ e2 ## S3 method for class 'Infix' e1 < e2 ## S3 method for class 'Infix' e1 > e2 ## S3 method for class 'Infix' e1 == e2 ## S3 method for class 'Infix' e1 >= e2 ## S3 method for class 'Infix' e1 <= e2 ## S3 method for class 'Infix' e1 & e2 ## S3 method for class 'Infix' !x ## S3 method for class 'Infix' as.environment(x)
## S3 method for class 'Print' print(x, ...) ## S3 method for class 'Infix' e1 + e2 ## S3 method for class 'Infix' e1 - e2 ## S3 method for class 'Infix' e1 / e2 ## S3 method for class 'Infix' e1 %% e2 ## S3 method for class 'Infix' e1 ^ e2 ## S3 method for class 'Infix' e1 < e2 ## S3 method for class 'Infix' e1 > e2 ## S3 method for class 'Infix' e1 == e2 ## S3 method for class 'Infix' e1 >= e2 ## S3 method for class 'Infix' e1 <= e2 ## S3 method for class 'Infix' e1 & e2 ## S3 method for class 'Infix' !x ## S3 method for class 'Infix' as.environment(x)
x |
an object |
... |
arguments passed to the local print method. |
e1 |
lhs operand |
e2 |
rhs operand |
The lhs is coerced with as.environment
and in that environment the binary operators must be found and named as .<binaryOperator>
(see the example for retList
). This is implemented for the following operators: +, -, *, /, %%, ^, <, >, ==, >=, <=, &
. Also part of the operators you can implement with Infix is !
, although it is unary.
This is a virtual class to be contained in other class definitions. It overrides the default subset functions $
and [[
such that private member of a class can not be accessed. Private is every object which has a name with a leading "." (grepl("^\\.", name)
). After this check the standard method for class 'envRefClass' is called or an error is reported.
## S4 method for signature 'Private' x$name ## S4 replacement method for signature 'Private' x$name <- value ## S4 method for signature 'Private' x[[i, j, ...]] ## S4 replacement method for signature 'Private' x[[i, j, ...]] <- value
## S4 method for signature 'Private' x$name ## S4 replacement method for signature 'Private' x$name <- value ## S4 method for signature 'Private' x[[i, j, ...]] ## S4 replacement method for signature 'Private' x[[i, j, ...]] <- value
x |
the object |
name |
name of field or method |
value |
any object |
i |
like name |
j |
ignored |
... |
ignored |
ClassWithPrivateField <- defineRefClass({ Class <- "ClassWithPrivateField" contains <- "Private" .p <- "numeric" getP <- function() .p setP <- function(v) .self$.p <- v }) test <- ClassWithPrivateField() stopifnot(inherits(try(test$.p), "try-error")) stopifnot(inherits(try(test$.p <- 2), "try-error")) stopifnot(inherits(try(test[[".p"]]), "try-error")) stopifnot(inherits(try(test[[".p"]] <- 2), "try-error"))
ClassWithPrivateField <- defineRefClass({ Class <- "ClassWithPrivateField" contains <- "Private" .p <- "numeric" getP <- function() .p setP <- function(v) .self$.p <- v }) test <- ClassWithPrivateField() stopifnot(inherits(try(test$.p), "try-error")) stopifnot(inherits(try(test$.p <- 2), "try-error")) stopifnot(inherits(try(test[[".p"]]), "try-error")) stopifnot(inherits(try(test[[".p"]] <- 2), "try-error"))
These functions are used internally. You should not rely on them. Use public
instead.
publicFunction(fun) publicValue(x = NULL, validity = function(x) TRUE) ## S4 method for signature 'publicEnv' x$name
publicFunction(fun) publicValue(x = NULL, validity = function(x) TRUE) ## S4 method for signature 'publicEnv' x$name
fun |
function definition |
x |
a default value |
validity |
an optional validity function for the set method. Returns TRUE or FALSE. |
name |
name of member in refernece object |
This functions can be used to construct a list with class attribute and
merged with another list called super. The constructed list will contain (by
default) all visible objects from the environment from which retList
is called.
retList(class = NULL, public = ls(envir), super = list(), superEnv = asEnv(super), mergeFun = envMerge, envir = parent.frame()) funNames(envir = parent.frame()) asEnv(x) stripSelf(x)
retList(class = NULL, public = ls(envir), super = list(), superEnv = asEnv(super), mergeFun = envMerge, envir = parent.frame()) funNames(envir = parent.frame()) asEnv(x) stripSelf(x)
class |
character giving the class name. |
public |
character with the names to include. |
super |
a list/object to be extended. |
superEnv |
environment where new methods will live in. |
mergeFun |
function with two arguments. Knows how to join/merge
environments - |
envir |
this is the environment you want to convert into the list. Default is the environment from which the function is called. |
x |
a list |
funNames
returns the names of functions in the environment
from which it is called.
asEnv
trys to find an environment for x. If x is NULL or an
empty list, the function returns NULL
. (Else) If x has an attribute
called .self
it is this attribute which is returned. (Else) If x is
a list it is converted to an environment.
# To get a quick overview of the package: vignette("Introduction", "aoos") # To get more infos about retList: vignette("retListClasses", "aoos") # To get some infos about performance: vignette("performance", "aoos") # A simple class with one method: Test <- function(.x) { getX <- function() .x retList("Test") } stopifnot(Test(2)$getX() == 2) # A second example inheriting from Test Test2 <- function(.y) { getX2 <- function() .x * 2 retList("Test2", super = Test(.y)) } stopifnot(Test2(2)$getX() == 2) stopifnot(Test2(2)$getX2() == 4) ### Rational numbers example with infix operators and print method Rational <- function(numer, denom) { gcd <- function(a, b) if(b == 0) a else Recall(b, a %% b) g <- gcd(numer, denom) numer <- numer / g denom <- denom / g print <- function(x, ...) cat(paste0(numer, "/", denom, "\n")) ".+" <- function(that) { Rational(numer = numer * that$denom + that$numer * denom, denom = denom * that$denom) } ".-" <- function(that) { if (missing(that)) { Rational(-numer, denom) } else { .self + (-that) } } # Return only what should be visible from this scope: retList(c("Rational", "Infix", "Print"), c("numer", "denom", "neg", "print")) } rational <- Rational(2, 3) rational + rational rational - rational
# To get a quick overview of the package: vignette("Introduction", "aoos") # To get more infos about retList: vignette("retListClasses", "aoos") # To get some infos about performance: vignette("performance", "aoos") # A simple class with one method: Test <- function(.x) { getX <- function() .x retList("Test") } stopifnot(Test(2)$getX() == 2) # A second example inheriting from Test Test2 <- function(.y) { getX2 <- function() .x * 2 retList("Test2", super = Test(.y)) } stopifnot(Test2(2)$getX() == 2) stopifnot(Test2(2)$getX2() == 4) ### Rational numbers example with infix operators and print method Rational <- function(numer, denom) { gcd <- function(a, b) if(b == 0) a else Recall(b, a %% b) g <- gcd(numer, denom) numer <- numer / g denom <- denom / g print <- function(x, ...) cat(paste0(numer, "/", denom, "\n")) ".+" <- function(that) { Rational(numer = numer * that$denom + that$numer * denom, denom = denom * that$denom) } ".-" <- function(that) { if (missing(that)) { Rational(-numer, denom) } else { .self + (-that) } } # Return only what should be visible from this scope: retList(c("Rational", "Infix", "Print"), c("numer", "denom", "neg", "print")) } rational <- Rational(2, 3) rational + rational rational - rational
This is a virtual class to be contained in other class definitions. It overrides the default show method and is intended to be used with the aoos class system (defineClass
). The show method will simply look for a method show
defined as member of a class definition.
## S4 method for signature 'Show' show(object)
## S4 method for signature 'Show' show(object)
object |
an object inheriting from |
ClassWithShowMethod <- defineClass("ClassWithShowMethod", contains = "Show", { show <- function() print(summary(.self)) }) ClassWithShowMethod()
ClassWithShowMethod <- defineClass("ClassWithShowMethod", contains = "Show", { show <- function() print(summary(.self)) }) ClassWithShowMethod()