Skip to content

Commit

Permalink
Hacking advanced reactivity around
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley committed Jul 3, 2019
1 parent f97f20c commit 464c356
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 136 deletions.
12 changes: 0 additions & 12 deletions _remnants.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,3 @@ This is the way things work by default in R, and it's called _lexical scoping_.
Shiny relies heavily on lexical scoping to allow reactive inputs, expressions, outputs, and observers to have relationships with each other, as we'll soon see.

## Prerequisite 2: Side effects

You can think of all R functions as falling into one of two categories:

1. **Functions that are only interesting for the result that they return**, e.g. `c()`, `list()`, `lm()`, `predict()`, `read.csv()`, `ls()`.
2. **Functions that perform some kind of action** (besides calculating a result), e.g. `print()`, `plot()`, `write.csv()`, `rm()`, `install.packages()`.

_Side effects_ are what we call the kinds of actions in the second category. Unlike with pharmaceuticals, where side effects are always unintentional and usually negative, we simply mean any effects apart from a function's return value. Changing a file on disk is a side effect. Printing words to the console is a side effect. Sending a message to another computer is a side effect.

As a shorthand, you can think of functions without side effects as _calculations_, and functions with side effects as _actions_.^[Computer scientists and Haskell programmers refer to functions without side effects as "pure" and functions with side effects as "impure"; though their definition of side effects includes the reading of any variable or state that isn't a function argument. For Shiny purposes, we don't need to be so strict.]

As we learn to write more complicated Shiny apps, it's important to think about your code in terms of calculations and actions. Shiny has opinions about what reactive abstractions you should use to wrap each kind of code, and if you structure your app in accordance with those opinions you'll end up with code that's more reliable, better performing, and easier to maintain.

14 changes: 0 additions & 14 deletions advanced-reactivity.Rmd
Original file line number Diff line number Diff line change
@@ -1,15 +1 @@
# (PART\*) Mastering reactivity {-}

# Introduction {-}

With traditional object-oriented UI programming, an inordinate amount of effort and complexity comes from explicitly managing the relationships between different pieces of UI, and updating the cascading sets of variables that depend on the UI and on each other. We want our code to be fast, correct, and easy to write/maintain. But when creating complex interactive data displays using traditional techniques, we're lucky to achieve even two out of those three.

In contrast, with just reactive inputs/values, reactive expressions, and observers/outputs, we have a rich vocabulary for expressing calculations and outputs, and that forms the foundation for a better way to do UI programming. As long as we can break up our logic into distinct-but-related calculations, actions, and outputs, Shiny's reactive framework will automatically handle the tedious and error-prone task of deciding what pieces need to be updated, and when.

Shiny is designed to let you get started writing apps without fully understanding how reactive programming works; you generally wrap conventional looking R code in `reactive({...})` or `renderXXX({...})`, and everything "just works". This contributes to the initial impression that Shiny is "magic", which thrills beginners but triggers immediate skepticism in expert programmers. Software feels magical when the user is unable to form a mental model for how it works; the greater the gap between what the software does and the user's ability to explain it does it, the more magical the software feels.

Unfortunately, magic in software usually leads to disillusionment. Without a solid mental model to reason with, it's extremely difficult to predict how the software will act when you venture beyond the borders of its demos and examples. And when things don't go the way you expect, debugging is almost impossible.

But sometimes there is good magic in software. As Tom Dale said of his Ember.js JavaScript framework: "We do a lot of magic, but it's _good magic_, which means it decomposes into sane primitives." This is the quality we aspire to for Shiny, especially when it comes to reactive programming. When you peel back the layers of reactive programming, you won't find a pile of heuristics, special cases, and hacks; but rather, a clever but ultimately fairly straightforward mechanism.

The goal of the chapters in this part of the book is to equip you with a solid understand of reactivity which will--I hope!--make it cease to feel like magic at all.
37 changes: 37 additions & 0 deletions getting-started.Rmd
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
# (PART\*) Getting started {-}

```{r include=FALSE}
source("common.R")
```

# Before we begin

If you've never used Shiny before, welcome! This chapter will help you install all the software you'll need to follow along with the book.

## What you will learn

Shiny is designed to let you get started writing apps without fully understanding how reactive programming works; you generally wrap conventional looking R code in `reactive({...})` or `renderXXX({...})`, and everything "just works". This contributes to the initial impression that Shiny is "magic", which thrills beginners but triggers immediate skepticism in expert programmers. Software feels magical when the user is unable to form a mental model for how it works; the greater the gap between what the software does and the user's ability to explain it does it, the more magical the software feels.

Unfortunately, magic in software usually leads to disillusionment. Without a solid mental model to reason with, it's extremely difficult to predict how the software will act when you venture beyond the borders of its demos and examples. And when things don't go the way you expect, debugging is almost impossible.

But sometimes there is good magic in software. As Tom Dale said of his Ember.js JavaScript framework: "We do a lot of magic, but it's _good magic_, which means it decomposes into sane primitives." This is the quality we aspire to for Shiny, especially when it comes to reactive programming. When you peel back the layers of reactive programming, you won't find a pile of heuristics, special cases, and hacks; but rather, a clever but ultimately fairly straightforward mechanism.

Shiny has demonstrated that reactive programming is a particularly good fit for apps that transform and/or visualize complex data. These kinds of apps seem to naturally decompose into discrete steps, like loading data, subsetting, aggregating, predicting, etc. These steps can be individually expressed using reactive objects, which can then be managed by Shiny.

## What you won't learn

I'll do my best to show off best practices for data science, R programming, and software engineering, but the focus of this book is on making effective Shiny apps and understanding the underlying theory of reactivity.

## Prerequisites {#prerequisites}

The first thing you'll need to do is install the software you'll need, if you don't have it already:
Expand Down Expand Up @@ -51,3 +69,22 @@ https://www.rstudio.com/resources/cheatsheets/
## If you get stuck

Got a problem you can't figure out, or a question this book (and Google) can't answer? You can find help at our community site: https://community.rstudio.com/c/shiny.

## Acknowledgements

This book was written in [bookdown](http://bookdown.org/) inside [RStudio](http://www.rstudio.com/ide/). The [website](http://mastering-shiny.org/) is hosted with [netlify](http://netlify.com/), and automatically updated after every commit by [travis-ci](https://travis-ci.org/). The complete source is available from [GitHub](https://github.com/jcheng/shiny-book).

```{r, echo = FALSE}
ruler <- function(width = getOption("width")) {
x <- seq_len(width)
y <- dplyr::case_when(
x %% 10 == 0 ~ as.character((x %/% 10) %% 10),
x %% 5 == 0 ~ "+",
TRUE ~ "-"
)
cat(y, "\n", sep = "")
cat(x %% 10, "\n", sep = "")
}
ruler()
```
Loading

0 comments on commit 464c356

Please sign in to comment.