-
Notifications
You must be signed in to change notification settings - Fork 19
/
whyNamespaces.Rmd
128 lines (106 loc) · 3.26 KB
/
whyNamespaces.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
---
title: "Namespaces - why"
author: "Martin Maechler"
date: "September 2015; last update Oct.20, 2016"
output: ioslides_presentation
---
## Why do we need **namespaces** in R ?
Short answer: __To prevent name conflicts and to allow modular programming in packages__
Example from Luke Tierney (2003)'s introductory article on namespaces
```{r}
mydnorm <- function(z)
1/sqrt(2 * pi) * exp(- z^2 / 2)
mydnorm(-2:2)
x <- seq(-5,5, length=100)
all.equal(dnorm(x), mydnorm(x))
```
##
So our function `mydnorm()` is ok,
the same as the R builtin `dnorm()`. But it is _less_ safe :
```{r}
pi <- 3
mydnorm(-2:2)
```
and these numbers are all wrong,
because R finds the wrong `pi` in `search()` :
```{r, pi-search}
find("pi")
search()
```
## R's `search()` path
R finds your object in `search()` - and uses the first found
```{r}
search()
conflicts()
find("pi")
```
What if you use many packages? You get even more conflicts
## Packages in `search()` - conflicts
```{r, Hmisc}
require("Hmisc")
```
## More packages ... more conflicts
```{r, dplyr}
require("dplyr")
```
```{r, dplyr-detach, echo=FALSE}
detach("package:dplyr")
```
## require("dplyr") .. [continued]
<!-- needs package `sfsmisc`, R-forge version >= 2015-09-05: -->
```{r, dplyr-2, echo=FALSE}
require("sfsmisc") # for the function capture.and.write()
capture.and.write(suppressWarnings(require("dplyr")), first = 1, last = 15)
```
## Conflicts in search() -- unavoidable
```{r,pkg-conflicts}
conflicts()
head(search())
```
The *solution*: --> *Namespace*s : ...
## Solution: Namespaces
*Namespace*s : complete protection of the functions in a package
from "re-definitions" in "globalenv" or other packages:
- A package must declare where it **imports** the functions (and objects, e.g., `pi`) it uses.
- The package also can use many **helper** functions and only **export** the main functions
- Consequence: Much more modular programming; clean interfaces
## Namespaces -- `::` (and `:::`)
Here, our `mydnorm()` is not in a package, but we can still __protect__ it
from redefinition of `pi`, because we can use the __pkg__`::`__name__
notation:
```{r, two-pis}
c(pi, base::pi)
```
and replace `pi` by `base::pi` within the `body` of our `mydnorm`:
```{r, fixed-body}
dnormOk <- mydnorm
body(dnormOk) <- do.call(substitute,
list(body(mydnorm), list(pi = quote(base::pi))))
```
where we have seen the very powerful combination of two important R functions,
`do.call()` and `substitute()`.
## The `dnormOK()` solution continued
```{r dnorm-fixed, collapse=TRUE}
dnormOk
all.equal(dnormOk(x), dnorm(x))
```
so `normOk` is ok, in spite of `pi` which of course still breaks `mydnorm()`:
```{r mydnorm-still-broken, collapse=TRUE}
pi
t <- -2:2; rbind(t, dnormOk(t), mydnorm(t))
```
## Packages & namespaces in R session
```{r, session-show, eval=FALSE}
sessionInfo() ## note the " loaded via namespace (and not attached) "
```
```{r, session-do, echo=FALSE}
# simple, but does not fit on one slide:
print( sI <- sessionInfo() , locale=FALSE)
```
## loaded ... (and not attached)
`sessionInfo()`
<!-- the following needs package `sfsmisc`, R-forge version >= 2015-09-05: -->
```{r, only-namespace, echo=FALSE}
capture.and.write(sI, first = 1, last = 12)
```
_Note_: The source is in MM's `r knitr::current_input(dir=TRUE)`