knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

Use

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)

Examples

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)

Subset the module to access its objects.

my$a
my$b
my$f(my$a, my$b)

Use with() to aceess the objects with their bare names.

with(my, 
     f(a,b))

Attach a Module to the Search Path

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()

And you can use the objects inside directly, just like those from a package.

f(a,b)

Detach the module from the search path when done, if desired.

drop("my")

Make objects Available to another Module

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)

my_other<- module({
        refer(my)
        c <- 4
        d <- 5
        f <- function() print("foo") 
})

ls(my_other)
my_other$f()

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 fs are available.

my_other <- module({
        refer(my, prefix = .)
        c <- 4
        d <- 5
        f <- function() print("foo") 
})

ls(my_other)

my_other$my.f(1, 2)
my_other$f()

Public and Private Variables

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)
room_101$..diary
room_101$get_diary()

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)
room_102$open_info
room_102$classified_info
room_102$get_classified()

Use a package

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.

mpg_analysis <- module({
    require(ggplot2)
    plot <- qplot(mtcars$mpg)    
})
mpg_analysis$plot

Meanwhile, the global search path remain unaffected, not containing the ggplot2 package:

"package:ggplot2" %in% search()

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

Simulate OOP

In this example, Bobby gets hungry, Dad complains and calls Mom:

bobby <- mod::ule({
        age_now <- 14
        favorite_food <- c("hamburger", "pizza", "chow mein")
        hungry_for <- function() {
          set.seed(Sys.Date())
          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()
dad$complain()
dad$call_mom()

It is imperative that mod be only adopted in simple cases; no mutable state is allowed in modules. If full-featured OOP is desired, use R6. The user is reminded that OOP, in general, should be used sparingly and with deliberation.

Notes

Environment

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)
is.environment(my)

Terms

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.

Locked

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.

my$a <- 1
my$new_var <- 1

Hidden Objects

As a general R rule, names that start with . define hidden objects.

my_yet_another <- module({
        .var <- "I'm hidden!"
})

Hidden objects are not returned by ls(), unless specified.

ls(my_yet_another)
ls(my_yet_another, all.names = TRUE)

Nonetheless, in a module, they are treated the same as public objects.

my_yet_another$.var

Load/Attach from File

module_path <- system.file("misc/example_module.R", package = "mod")

To load and assign to object:

example_module <- acquire(module_path)
ls(example_module)
example_module$a
example_module$d()
example_module$e(100)

To load and attach to search path:

use(module_path)
ls("module:example_module")
a
d()
e(100)

Modules and Packages

As it could be confusing how to deal with modules and packages, the following clarification is made:

*: 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.

Unattached

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:

detach("package:mod")

my_mind <- mod::ule({
  provide(good_thought)
  good_thought <- "I love working on this package!"
  bad_thought <- "I worry that no one will appreciate it."
})

mod::use(my_mind)
good_thought


iqis/ntr documentation built on Dec. 7, 2019, 10:20 p.m.