Title: | Minimal Implementation of Functional Lenses |
---|---|
Description: | Provides utilities to create and use lenses to simplify data manipulation. Lenses are composable getter/setter pairs that provide a functional approach to manipulating deeply nested data structures, e.g., elements within list columns in data frames. The implementation is based on the earlier 'lenses' R package <https://github.com/cfhammill/lenses>, which was inspired by the Haskell 'lens' package by Kmett (2012) <https://github.com/ekmett/lens>, one of the most widely referenced implementations of lenses. For additional background and history on the theory of lenses, see the 'lens' package wiki: <https://github.com/ekmett/lens/wiki/History-of-Lenses>. |
Authors: | Albert Wang [aut, cre, cph] |
Maintainer: | Albert Wang <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.1.0 |
Built: | 2024-12-10 01:36:22 UTC |
Source: | CRAN |
The resulting lens first applies the left lens, then the right lens.
l %.% m
l %.% m
l |
First lens |
m |
Second lens |
A new lens
d <- list(list(a = 1, b = 2), list(a = 4, b = 9)) l <- index_l(1) m <- index_l("b") view(d, l %.% m)
d <- list(list(a = 1, b = 2), list(a = 4, b = 9)) l <- index_l(1) m <- index_l("b") view(d, l %.% m)
Lens into a named attribute of an object.
attr_l(name)
attr_l(name)
name |
Name of the attribute to lens into |
A lens that selects the specified attribute
x <- 1:10 attr(x, "label") <- "my_label" l <- attr_l("label") view(x, l) set(x, l, "new_label")
x <- 1:10 attr(x, "label") <- "my_label" l <- attr_l("label") view(x, l) set(x, l, "new_label")
Convenience function that mirrors purrr::pluck()
.
c_l(...)
c_l(...)
... |
A sequence of lenses and/or integers/logical vectors |
A lens that combines all specified lenses (left to right).
d <- list(a = list(b = 1, c = 2), b = list(b = 3, c = 4)) l <- c_l("a", "b") view(d, l)
d <- list(a = list(b = 1, c = 2), b = list(b = 3, c = 4)) l <- c_l("a", "b") view(d, l)
This function returns an illegal lens that filters according to the specified conditions.
filter_il(...)
filter_il(...)
... |
Conditions to filter by |
Conditions are evaluated in the context of the data frame.
A lens that filters the specified rows
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- filter_il(x > 5) # get the rows where x is greater than 5 view(d, l) # set the rows where x is greater than 5 to 8 set(d, l, 8) # set y value to 8 where x is greater than 5 set(d, l %.% select_l(y), 8)
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- filter_il(x > 5) # get the rows where x is greater than 5 view(d, l) # set the rows where x is greater than 5 to 8 set(d, l, 8) # set y value to 8 where x is greater than 5 set(d, l %.% select_l(y), 8)
Trivial identity lens: returns and sets the object itself.
id_l
id_l
An object of class tinylens::lens
(inherits from S7_object
) of length 1.
x <- 1:10 view(x, id_l)
x <- 1:10 view(x, id_l)
Lens into a single element of a list.
index_l(i)
index_l(i)
i |
Index of the element to lens into |
This lens performs indexing using double bracket notation, i.e., x[[i]]
.
A lens that selects the specified element
x <- list(a = 1, b = 2) l <- index_l("a") view(x, l)
x <- list(a = 1, b = 2) l <- index_l("a") view(x, l)
This function returns a lens that subsets the object in a generalized way.
indices_l(...) i_l(...)
indices_l(...) i_l(...)
... |
Conditions to subset by. Unnamed arguments are used as indices.
Named arguments are passed along to |
A lens that subsets the object by the specified indices
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- indices_l(1, 1) # get the first row of first column view(d, l) # set the first row of first column set(d, l, 1) # get the first row l <- indices_l(1,) view(d, l) # set the first row set(d, l, 1)
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- indices_l(1, 1) # get the first row of first column view(d, l) # set the first row of first column set(d, l, 1) # get the first row l <- indices_l(1,) view(d, l) # set the first row set(d, l, 1)
A lens is a pair of functions that can be used to view and set a value in an object. Lenses are implemented as S7 classes.
lens(view, set = NULL)
lens(view, set = NULL)
view |
A function that takes an object and returns a value |
set |
A function that takes an object and a value and returns a new object |
A "proper" lens should satisfy the following so-called "lens laws":
View-Set: set(d, l, view(d, l)) == d
Set-View: view(set(d, l, x), l) == x
Set-Set: set(set(d, l, x), l, y) == set(d, l, y)
These laws are not enforced by tinylens
, but you should strive to follow them
when creating your own lenses.
A best effort has been made to ensure that these laws hold for the lenses
provided by tinylens
, but this is trickier than it might seem because of
how R handles subset assignments.
A lens with the specified view and set functions
# create a trivial identity lens l <- lens(view = function(x) x, set = function(x, value) value)
# create a trivial identity lens l <- lens(view = function(x) x, set = function(x, value) value)
This lens allows you to access and modify elements of a list or vector based on their position or a logical condition.
map_l(l, .ptype = NULL)
map_l(l, .ptype = NULL)
l |
A lens that selects the elements to lens into |
.ptype |
The prototype of the data structure to return |
A lens that selects the specified elements
d <- list(list(a = 1, b = 2), list(a = 4, b = 9)) l <- index_l("a") view(d, map_l(l)) over_map(d, map_l(l), sqrt)
d <- list(list(a = 1, b = 2), list(a = 4, b = 9)) l <- index_l("a") view(d, map_l(l)) over_map(d, map_l(l), sqrt)
Lens into the names
attribute of an object. This uses rlang::names2
to
better handle NULL
names.
names_l
names_l
An object of class tinylens::lens
(inherits from S7_object
) of length 1.
x <- letters[1:10] names(x) <- letters[1:10] view(x, names_l) over(x, names_l, toupper)
x <- letters[1:10] names(x) <- letters[1:10] view(x, names_l) over(x, names_l, toupper)
Modify the focused part of a data structure
over(d, l, f)
over(d, l, f)
d |
The data structure to view |
l |
The lens to apply |
f |
The function to apply |
The modified data structure
Apply a function to each element of a list returned by a lens. Using over
in such cases would require a "lifted" function, which is often unergonomic.
over_map(d, l, f)
over_map(d, l, f)
d |
The data structure to modify |
l |
The list-returning lens to apply |
f |
The function to apply to each element of the list |
The modified data structure
d <- list(list(a = 1, b = 2), list(a = 4, b = 9)) l <- map_l(index_l("a")) over_map(d, l, sqrt)
d <- list(list(a = 1, b = 2), list(a = 4, b = 9)) l <- map_l(index_l("a")) over_map(d, l, sqrt)
This function returns a lens that selects the specified rows.
rows_l(idx)
rows_l(idx)
idx |
The rows to select |
A lens that selects the specified rows
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- rows_l(1:2) # get the first two rows view(d, l) # set the first two rows set(d, l, 1:2)
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- rows_l(1:2) # get the first two rows view(d, l) # set the first two rows set(d, l, 1:2)
This function returns a lens that selects the specified columns. Requires
tidyselect
to be installed.
select_l(...)
select_l(...)
... |
Columns to select |
A lens that selects the specified columns
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- select_l(x, y) # get the x and y columns view(d, l) # set the x and y columns set(d, l, 1)
d <- data.frame(x = 1:10, y = 11:20, z = 21:30) l <- select_l(x, y) # get the x and y columns view(d, l) # set the x and y columns set(d, l, 1)
Set the focused part of a data structure
set(d, l, x)
set(d, l, x)
d |
The data structure to view |
l |
The lens to apply |
x |
The value to set |
The modified data structure
Lens into a slice of a vector.
slice_l(idx)
slice_l(idx)
idx |
Indices of the elements to lens into |
This lens performs indexing using single bracket notation, i.e., x[idx]
.
A lens that selects the specified slice
x <- letters[1:10] l <- slice_l(1:5) view(x, l)
x <- letters[1:10] l <- slice_l(1:5) view(x, l)
Allows mutation of vector data while preserving attributes, e.g., labels or names.
vec_data_l
vec_data_l
An object of class tinylens::lens
(inherits from S7_object
) of length 1.
x <- letters[1:10] names(x) <- letters[1:10] # toy function that strips names; most functions from `stringr` do this f <- function(x) toupper(unname(x)) # apply the function without losing attributes over(x, vec_data_l, f)
x <- letters[1:10] names(x) <- letters[1:10] # toy function that strips names; most functions from `stringr` do this f <- function(x) toupper(unname(x)) # apply the function without losing attributes over(x, vec_data_l, f)
view()
applies a lens to a data structure and returns the focused part.
set()
applies a lens to a data structure and sets the focused part.
over()
applies a lens to a data structure and modifies the focused part using a function.
view(d, l)
view(d, l)
d |
The data structure to view |
l |
The lens to apply |
The part of the data structure focused by the lens
x <- 1:10 names(x) <- letters[1:10] view(x, names_l) set(x, names_l, LETTERS[1:10]) over(x, names_l, toupper)
x <- 1:10 names(x) <- letters[1:10] view(x, names_l) set(x, names_l, LETTERS[1:10]) over(x, names_l, toupper)
Illegal lens into elements of a vector that satisfy a predicate.
where_il(p)
where_il(p)
p |
A predicate function |
A lens that selects the elements that satisfy the predicate
d <- 1:10 l <- where_il(\(x) x %% 2 == 0) view(d, l) over(d, l, \(x) x / 2)
d <- 1:10 l <- where_il(\(x) x %% 2 == 0) view(d, l) over(d, l, \(x) x / 2)