llr

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!

Installation

remotes::install_github("dirkschumacher/llr")

Intro

(->
  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.

REPL

It also has a (limited) REPL

interp <- llr_env$new()
interp$repl()

Special forms

Data Types

Lists

; this is a list
'(1 2 3 4 5 6)
; an unquoted list is a function call
(+ 1 2 3 4 5 6)
#> 21

Vectors

[1 2 3 4]
#> [1 2 3 4]

Maps

{:a 1 :b 2}
#> {:a 1 :b 2}

Symbols

x
namespaced.variable/x
:keyword
"character"
10 ; integer
10.42 ; double

Functions

(fn [a b] (+ a b))

(fn this
  ([] 0)
  ([a] a)
  ([a b] (+ a b))
  ([a b & more] (reduce + (concat [a b] more))))

def

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

Meta-data

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

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

Recursion

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

Namespaces

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

Reader Dispatch

The reader switches to a different set of interpretations of the next symbol when reading the character #.

#_ ignores the next form

#_ (r/stop "error")
"Yay"
#> "Yay"

R interop

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

Design Goals

  • 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.

Contributing

  • 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.

Code of Conduct

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.

Copy Link

Version

Down Chevron

Version

0.3.0.9000

License

MIT + file LICENSE

Maintainer

Last Published

January 1st, 1970