-
Notifications
You must be signed in to change notification settings - Fork 128
Expand file tree
/
Copy pathdevelop.Rmd
More file actions
28 lines (17 loc) · 4.76 KB
/
develop.Rmd
File metadata and controls
28 lines (17 loc) · 4.76 KB
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
# (PART) Developer-facing topics {-}
This part describes some advanced topics some R developers may want to know regarding the **plotly** package. Some of the content found here may be useful for the following people:
* R developers that have authored a custom __ggplot2__ geom and want to inform `ggplotly()` about the rendering rules of their geom.
* R developers that want to build a similar interface to another JavaScript graphing library.
# Designing an htmlwidget interface
The plotly.js library, as with many other JavaScript graphing libraries, strives to describe any plot through a plot specification defined via JavaScript Object Notation (JSON). JSON is a language independent data-interchange format that was originally designed for JavaScript, but parsers for many different languages now exist, including R [@RJSONIO; @jsonlite]. JSON is a recursive key-value data structure (similar to a list in R), and essentially any valid JavaScript value has a natural R equivalent (e.g., `NULL`/`null`). As a result, any JSON object can be created from an appropriate R list, meaning that theoretically any plotly.js plot can be described via an R list. However, simply providing a bridge between R lists and JSON does not guarantee a powerful or usable interface, especially for a general purpose graphing library.
Although it can be complicated to implement, R interfaces to JavaScript graphing libraries should leverage R's strong resources for computing on the language to design a more expressive interface [@adv-r]. It should also look and feel like (and work well with!) other commonly used interfaces in R. A good way to do this is to embrace (pure and predictable) functional programming. Most importantly, this implies that every function _modifies_ a central type of object -- meaning that every function input and output the same type of object (predictable). Furthermore, if the output of a function can be determined completely by the input (i.e., pure), it removes any need to search for other code that may be affecting the output. In the case of providing an interface to a JavaScript graphing library, there are a number of reasons why the central object should inherit from the central object provided by the **htmlwidgets** package.
The idea of interfacing R with JavaScript libraries via JSON data transfer has been popular approach for quite some time [@rCharts; @animint; @Sievert:2014b]. The R package **htmlwidgets** standardized this bridge, and provides some additional infrastructure for making sure the HTML output works as expected in multiple contexts (in the R console or RStudio, within **rmarkdown** documents, and even embedded inside **shiny** apps). The **htmlwidgets** package itself is opinionated about the data structure used to represent the widget in R since it needs to retain meta-information about the widget, such as the sizing policy. To avoid surprise, widget authors should strive to have all functions in their interface modify this data structure.^[The __plotly__ package initially fought this advice and represented plotly objects using a special data frame with a special print method to enable the [data-plot-pipeline](#data-plot-pipeline). I have since changed my mind and decided special methods for popular generic functions should be provided instead.]
JavaScript graphing libraries usually have strong requirements about the JSON structure used to create a plot. In some cases, the R interface needs to know about these requirements in order to faithfully translate R objects to JSON. For example, in plotly.js some attributes must _always_ be an array (e.g., x/y), even if they are length 1, while other attributes cannot be an array must be a literal constant (e.g., name). This leads to a situation where the translation rules from R to JSON cannot be simply "box all vectors of length 1 into an array (or not)":
```javascript
list(x = 1, y = 1, name = "A point") => {x: [1], y: [1], name: "A point"}
```
Thankfully plotly.js provides a plot schema which declares types for each attribute that __plotly__ leverages internally. If necessary, __plotly__ tries to coerce each attribute to its expected type at print time, and also searches for any unsupported attributes that may have been specified by the user (and throws a warning that the attribute will be ignored). This helps informs users when they've mis-specified plotly.js attributes from R.
<!-- TODO:
* using R's data types to provide smart defaults that simply aren't possible with JSON
The **htmlwidgets** package also provides ways for both widget authors and users to extend the functionality of the underlying JavaScript library. In fact, the **plotly** package uses this mechanism to extend the plotly.js graphing library and enable all the material in [Advanced interactive techniques](advanced-interactive-techniques).
-->