```r
library(schemeR, warn.conflicts=FALSE) library(knitr)
options(width=80) knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "README-", warning = FALSE, message = FALSE, echo = TRUE, tidy = FALSE, size="small", linewidth=80 )
hook_output = knit_hooks$get('output') knit_hooks$set(output = function(x, options) { # this hook is used only when the linewidth option is not NULL if (!is.null(n <- options$linewidth)) { x = knitr:::split_lines(x) # any lines wider than n should be wrapped if (any(nchar(x) > n)) x = strwrap(x, width = n) x = paste(x, collapse = '\n') } hook_output(x, options) })
[](https://travis-ci.org/wwbrannon/schemeR) [](https://cran.r-project.org/package=schemeR) [](https://cran.r-project.org/package=schemeR) [](https://wwbrannon.mit-license.org/) # schemeR The schemeR package provides a Lisp-like mini-language within R, with a prefix syntax and versions of many of the usual Lisp operators (let, cond, do, functional programming utilities, etc.). Code written with this package is still valid R, but the differences from what R code normally looks like highlight the language's roots in Scheme. Real Lisp macros (the "non-hygienic", defmacro-based variety) are also supported. The implementation hews more closely to the way macros work in Common Lisp than other macro implementations for R, such as the one in the gtools package. Future work on schemeR will include an exploration of hygienic macros and whether they can be implemented as well. ## Basic usage The main entry point is the `schemeR()` function, which takes code written in this package's prefix style, translates it to the usual R syntax, and executes it. For example, here are prefix and infix versions of a simple loop (the `invisible` is to hide final return values): ``` {r, echo=TRUE} x <- sort(sample(1:10, 5, replace=TRUE)) # In the usual infix R syntax: invisible( for(i in x) print(1:i) ) #Equivalent, but prefix: schemeR({ .(invisible, .(`for`, i, x, .(print, .(`:`, 1, i)))) })
The .
that appears in the prefix code is a function, which is how we can manage to write R this way and still have it parse. The unusual-for-Lisp syntax on display in prefix code is a consequence: lists must start with a .(
rather than just a parenthesis, and elements are separated by commas rather than whitespace (because as far as R's parser is concerned, they're function arguments).
Despite its usefulness, the .
function is just syntactic sugar: it generates a call to its first argument with all of its subsequent arguments, and requires a preprocessing step to be turned back into usable code. schemeR()
does that preprocessing, with a few additional pieces of syntactic sugar, and executes the code that results.
schemeR()
understands a few pieces of infix syntactic sugar:
R(
and the matching )
will be executed as-is, rather than being transformed back to the usual R syntax first;.b(
rather than .(
are implicitly quasiquoted, just like the backtick in Lisp and Scheme. Because they're transformed into calls to quasiquote
(see the section on macros below), its .c()
and .s()
notation for unquote and unquote-splicing can be used.A variety of Lisp operators are also included, a few of which are just syntactically convenient aliases for things in base R (to avoid backtick-quoting, so that for example one can write progn
instead of `{`
). Some of the included operators are:
This package's implementation of macros is closer to Common Lisp's version than other R implementations are. Specifically,
bquote
) provides both unquote and unquote-splicing. Expressions to unquote are surrounded by .c()
(for "comma", as in Lisp) and expressions to unquote-splice are surrounded by .s()
(for "splice"). bquote
provides only .()
, which is the equivalent of .c()
.gensym
, as in Common Lisp, builds temporary symbols which macros can use to avoid shadowing variables in the containing scope;substitute()
to automatically interpolate parameters into their body expressions. The greater control over the macro's expansion makes it easier to write in an idiomatic Lisp style, and in particular allows for finer control of quoting and the use of gensym
.A working example of using macros demonstrates most package features. The macro below implements Python's list comprehensions:
schemeR({ .(defmacro, list.of, .(exp, s1, var, s2, lst, s3, test), .(cond, .(.(missing, test), .(set, test, TRUE))), .(let, .(.(result, .(gensym, "G1")), .(lvar, .(gensym, "G2"))), .b(.(let, .(.(.c(lvar), .c(lst))), .(do, .(.(.c(result), nil, .(cond, .(.c(test), .(append, .c(result), .c(exp))), .(TRUE, .c(result)))), .(.c(var), car(.c(lvar)), car(.c(lvar)))), .(.(is.nil, .c(lvar)), .c(result)), .(sset, .c(lvar), .(cdr, .c(lvar)))))))) }) # List comprehensions! list.of(x^2, `for`, x, `in`, 1:10) list.of(x^2, `for`, x, `in`, 1:10, `if`, x >= 5) # But they look a bit more natural in prefix form: schemeR( .(list.of, x^2, `for`, x, `in`, 1:10) ) schemeR( .(list.of, x^2, `for`, x, `in`, 1:10, `if`, .(`>=`, x, 5)) ) # You have to backtick-quote "for", "in" and "if" because they're R reserved # words, but you're free to use syntactic names if you want, because the macro # discards those arguments anyway: schemeR( .(list.of, x^2, with, x, of, 1:10) ) schemeR( .(list.of, x^2, with, x, of, 1:10, where, .(`>=`, x, 5)) )
Install the released version from CRAN:
install.packages("schemeR")
Install the dev version from github:
install.packages("devtools") devtools::install_github("wwbrannon/schemeR")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.