The mod
package is designed to be used either attached or unattached to your search path.
The following demonstrations show the package attached:
require(mod)
#> 载入需要的程辑包:mod
#>
#> 载入程辑包:'mod'
#> The following object is masked from 'package:base':
#>
#> drop
Define an inline module:
my <- module({
a <- 1
b <- 2
f <- function(x, y) x + y
})
The resulting module contains the objects defined within.
ls(my)
#> [1] "a" "b" "f"
Subset the module to access its objects.
Use with()
to aceess the objects with their bare names.
with(my,
f(a,b))
#> [1] 3
Just like a package, a module can be attached to the search path.
use(my)
The my
module is attached to the search path as “module:my”.
search()
#> [1] ".GlobalEnv" "module:my" "package:mod"
#> [4] "package:stats" "package:graphics" "package:grDevices"
#> [7] "package:utils" "package:datasets" "package:methods"
#> [10] "Autoloads" "package:base"
And you can use the objects inside directly, just like those from a package.
Detach the module from the search path when done, if desired.
drop("my")
Use refer()
to “copy” objects from another module. In the following example, we create a new module my_other
that uses the objects from my
, which is previsouly defined.
ls(my)
#> [1] "a" "b" "f"
my_other<- module({
refer(my)
c <- 4
d <- 5
f <- function() print("foo")
})
ls(my_other)
#> [1] "a" "b" "c" "d" "f"
my_other$f()
#> [1] "foo"
In addition to its own objects, my_other
module has all objects from my
, except f
: because my_other
module also has a f
object, and replaces the f
from my
module.
We can re-define my_other
and prepend objects from my
with my. This way, both f
s are available.
A variable is private if its name starts with ..
.
room_101 <- module({
..diary <- "Dear Diary: I used SPSS today..."
get_diary <- function(){
..diary
}
})
A private variable cannot be seen or touched. There is no way to directly access ..diary
from the outside, except by a function defined within the module, get_diary()
. This can be useful if you want to shield some information from the other users or programs.
ls(room_101)
#> [1] "get_diary"
room_101$..diary
#> NULL
room_101$get_diary()
#> [1] "Dear Diary: I used SPSS today..."
If using provide()
function to explicitly declair public variables, all others become private.
room_102 <- module({
provide(open_info, get_classified)
open_info <- "I am a data scientist."
classified_info <- "I can't get the database driver to work."
get_classified <- function(){
classified_info
}
})
ls(room_102)
#> [1] "get_classified" "open_info"
room_102$open_info
#> [1] "I am a data scientist."
room_102$classified_info
#> NULL
room_102$get_classified()
#> [1] "I can't get the database driver to work."
The mod
package provides a require()
function. mod:::require()
works in the same manner as do base::require()
, but makes a packages available for use in its containing module only.
Meanwhile, the global search path remain unaffected, not containing the ggplot2
package:
"package:ggplot2" %in% search()
#> [1] FALSE
mod::ule
is simple, lightweight and staright-forward. It is plain R and contains zero GMO.
Modules live any any other R object.
You do things the R way. For example, acquire()
and module()
only returns a module object; It is up to you to put it into your working environemnt with <-
.d
The below example simulates one essential behavior of an object in Object-oriented Programming(OOP) by manipulating the state of ..count
.
counter <- module({
..count <- 0
add_one <- function(){
#Its necessary to use `<<-` operator.
..count <<- ..count + 1
}
reset <- function(){
..count <<- 0
}
get_count <- function(){
..count
}
})
A variable must be private to be mutable like, ..count
.
The following demonstrates:
# initial count: 0
counter$get_count()
#> [1] 0
# add_one() twice
counter$add_one()
counter$add_one()
# ..count is now 2
counter$get_count()
#> [1] 2
# reset ..count to 0
counter$reset()
# ..count is back to 0 again
counter$get_count()
#> [1] 0
In the next example, Bobby gets hungry, Dad complains and calls Mom:
bobby <- mod::ule({
age_now <- 14
favorite_food <- c("hamburger", "pizza", "chow mein")
set.seed(666)
hungry_for <- sample(favorite_food, 1)
cry <- function(){
sprintf("I just really really want some %s, now!!!", hungry_for)
}
})
dad <- mod::ule({
refer(bobby, prefix = .)
provide(complain, call_mom)
complain <- function(){
sprintf("When I was %d, I've already earned a living on the dock.",
bobby.age_now)
}
call_mom <- function(){
sprintf("Honey, can you stop by the convenience store and get some %s?",
bobby.hungry_for)
}
})
bobby$cry()
#> [1] "I just really really want some pizza, now!!!"
dad$complain()
#> [1] "When I was 14, I've already earned a living on the dock."
dad$call_mom()
#> [1] "Honey, can you stop by the convenience store and get some pizza?"
It is imperative that mod
be only adopted in simple cases. If full-featured OOP is desired, use R6
. The user is reminded that OOP, in general, should be used sparingly and with deliberation.
A module is an environment. This means that every rule that applies to environments, such as copy-by-reference, applies to modules as well.
mode(my)
#> [1] "environment"
is.environment(my)
#> [1] TRUE
Some may wonder the choice of terms. Why refer()
and provide()
? Further, why not import()
and export()
? This is because we feel import and export are used too commonly, in both R, and other popular languages with varying meanings. The popular reticulate
package also uses import()
. To avoid confusion, we decided to introduce some synonyms. With analogous semantics, refer()
is borrowed from Clojure, while provide()
from Racket; Both languages are R’s close relatives.
A module is locked by default. It is impossible to either change the value of a object or add a new object to a module.
module_path <- system.file("misc/example_module.R", package = "mod")
To load and assign to object:
example_module <- acquire(module_path)
ls(example_module)
#> [1] "a" "d" "e"
example_module$a
#> [1] 1
example_module$d()
#> [1] 6
example_module$e(100)
#> [1] 106
To load and attach to search path:
As it could be confusing how to deal with modules and packages, the following clarification is made:
use()
refer()
Depends
or Imports
the mod
package*: Modules cannot be attached to another module’s “seach path”. Use refer()
instead to make clearly visible objects in the module context. **: Objects cannot be batch-copied from a module to the global environment, use use()
to attach the module to the global search path in order to use them directly.
These two features seek to avoid one very important problem mod
packages intends to solve: conflicting names.
As aforementioned, the package is designed to be usable both attached and unattached.
If you use the package unattached, you must always qualify the object name with ::
, such as mod::ule()
, a shorthand for mod::module()
. However, while inside a module, the mod
package is always available, so you do not need to use ::
. Note that in the following example, provide()
inside the module expression is unqualified.
See: