llr is a small, work in progress and just for fun clojure-like lisp on top of R’s abstract syntax trees. Expressions are not interpreted, but are translated to R’s AST and then interpreted by the R interpreter.
Most implementation details are sub-optimal, but the focus is on having fun and producing results instead writing perfect code. There are also many bugs and inconsistencies!
remotes::install_github("dirkschumacher/llr")
(->
r/datasets::mtcars
(r/dplyr::filter (r/base::`>` hp 100))
(r/dplyr::summarise :count (r/dplyr::n) :mean_mpg (r/mean mpg))
(r/tibble::as_tibble))
#> # A tibble: 1 x 2
#> count mean_mpg
#> <int> <dbl>
#> 1 23 17.5
Or run it from R
library(llr)
interp <- llr_env$new()
interp$eval("(+ 1 1)")
#> 2
Also see some Advent Of Code solutions in llr.
It also has a (limited) REPL
interp <- llr_env$new()
interp$repl()
; this is a list
'(1 2 3 4 5 6)
; an unquoted list is a function call
(+ 1 2 3 4 5 6)
#> 21
[1 2 3 4]
#> [1 2 3 4]
{:a 1 :b 2}
#> {:a 1 :b 2}
x
namespaced.variable/x
:keyword
"character"
10 ; integer
10.42 ; double
(fn [a b] (+ a b))
(fn this
([] 0)
([a] a)
([a b] (+ a b))
([a b & more] (reduce + (concat [a b] more))))
def
defines a symbol in a namespace and assignes it a name.
(def x 1)
(def plus (fn [a b] (+ a b)))
(plus x x)
#> 2
Symbols and values can hold meta-data. That meta-data needs to be a map at the moment.
(def ^{:const true} x ^{:meta "hello"} [ 1 2 3])
(meta x)
#> {:meta "hello"}
Meta-data on symbols is currently only available to the reader.
Macros are also supported. Macros are functions bound to a name with
meta data {:macro true}
.
In a macro you can use syntax-quote <backtick>
together with the
unquote ~
and unquote-splice ~@
operators.
(defmacro infix [operand1 operator operand2]
`(~operator ~operand1 ~operand2))
(infix 1 + 1)
#> 2
Similar to Clojure llr uses recur
to jump to a recursion point
currently only defined by loop
.
(def is-even
(fn [number]
(loop [cnt number]
(if (zero? cnt)
true
(if (< cnt 0) false (recur (- cnt 2)))))))
#> `is-even`
(is-even 5001)
#> false
(is-even 5000)
#> true
Every top level definition is part of a namespace
(ns product.lib)
(defn compute [a b] (+ a b))
(ns user)
(product.lib/compute 10 32)
#> 42
The reader switches to a different set of interpretations of the next
symbol when reading the character #
.
#_ (r/stop "error")
"Yay"
#> "Yay"
All symbols starting with the namespace r/
are treated slightly
differently. You can use that to refer to external R functions and
symbols. In addition keywords are interpreted as named arguments.
(r/set.seed 1)
(def rand-numbers (r/stats::rnorm :n 10))
(r/mean rand-numbers)
#> [1] 0.1322028
- Have fun, experiment and learn :)
- Build a clojure-like language that supports R-interop using the
r/
namespace. - Thus the core language should feel like clojure and support some of clojures’s core functions, but still make it easy to work with R’s internal data structures.
- Please read the code-of-conduct and also be aware that this a fun project, so things will break and progress is valued prefect code (at the moment).
- However everyone is invited to play around with the language, learn together, extend it, document things, fix bugs and propose features.
Please note that the llr project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.