Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

complex language object #32

Closed
moodymudskipper opened this issue Sep 1, 2022 · 4 comments
Closed

complex language object #32

moodymudskipper opened this issue Sep 1, 2022 · 4 comments

Comments

@moodymudskipper
Copy link
Collaborator

moodymudskipper commented Sep 1, 2022

For now we rely on rlang::express_deparse(), which is better than base::deparse() but insufficient nevertheless.

These seem like artificial cases but :

  • They're necessary for S4 objects #31
  • The package might be used to debug complicated cases where dput or deparse are misleading, so extreme cases are not necessarily out of scope
# Problem 1 --------------------------------------------------------------------

# OK
x <- quote(a(1)(2))
attr(x, "foo") <- "bar"
constructive::construct(x)
#> quote(a(1)(2)) |>
#>   structure(foo = "bar")

# not OK
x <- quote(a(1)(2))
attr(x[[1]], "foo") <- "bar"
constructive::construct(x)
#> quote(a(1)(2))
#> Error in `constructive::construct()`:
#> ! {constructive} couldn't create code that reproduces perfectly the output
#> `attr(original[[1]], 'foo')` is a character vector ('bar')
#> `attr(recreated[[1]], 'foo')` is absent
#> ℹ use `check = FALSE` to ignore this error

# due to 
rlang::expr_deparse(x)
#> [1] "a(1)(2)"

# valid generation -------------------------------------------------------------

x <- quote(a(1)(2))
attr(x[[1]], "foo") <- "bar"

# works but we'll have problems with recursion
y <- bquote(
  .(structure(quote(a(1)), foo = "bar"))(2)
)
identical(x, y)
#> [1] TRUE

# a bit less pretty but no problem with recursion
y <- as.call(c(
  structure(quote(a(1)), foo = "bar"), 
  2
))
identical(x, y)
#> [1] TRUE

# Problem 2 --------------------------------------------------------------------

# OK
x <- quote(a(1))
x[[1]] <- quote(mean)
constructive::construct(x)
#> quote(mean(1))

# not OK
x <- quote(a(1))
x[[1]] <- mean
constructive::construct(x)
#> Error in `constructive::construct()`:
#> ! The code built by {constructive} could not be parsed.
#> Caused by error in `parse_safely()`:
#> ! <text>:1:7: unexpected '<'
#> 1: quote(<
#>           ^

# due to 
rlang::expr_deparse(x)
#> [1] "<function(x, ...) UseMethod(\"mean\")>(1)"

# valid generation -------------------------------------------------------------

x <- quote(a(1))
x[[1]] <- mean

# here maybe we can use bquote because no possible recursion
# still if the original code use `.()` this won't work, and only `as.call()` can save us there I think
y <- bquote(
  .(mean)(1)
)

identical(x, y)
#> [1] TRUE

Created on 2022-09-01 by the reprex package (v2.0.1)

@moodymudskipper
Copy link
Collaborator Author

Would be miraculously a 3 lines fix with rlang::is_expression() if it returned FALSE when the call hides attributes. See r-lib/rlang#1475

We might have to reprogram is_expression(), the logic doesn't seem extractable easily.
Or we implement another recursive logic but it won't be half as pretty.

@moodymudskipper
Copy link
Collaborator Author

moodymudskipper commented Sep 2, 2022

There seems to be a logic in rlang to avoid recursion, probably for some stack issues since they stop at 100 anyway. But for our use case this should do it:

is_expression2 <- function(x) {
  if(!is.null(attributes(x))) return(FALSE)
  if (rlang::is_syntactic_literal(x) || rlang::is_symbol(x)) return(TRUE)
  if(!rlang::is_call(x)) return(FALSE)
  all(vapply(x, is_expression2, logical(1)))
}

And we tweak it slightly because we're fine with the top level having attributes :

is_expression2 <- function(x) {
  if (rlang::is_syntactic_literal(x) || rlang::is_symbol(x)) return(TRUE)
  if(!rlang::is_call(x)) return(FALSE)
  all(vapply(x, function(x) is.null(attributes(x)) && is_expression2(x), logical(1)))
}

It works :) :

library(constructive)
x <- quote(a(1)(2))
attr(x[[1]], "foo") <- "bar"
construct(x)
#> as.call(list(
#>   quote(a(1)) |>
#>     structure(foo = "bar"),
#>   2
#> ))

y <- quote(a(1))
y[[1]] <- mean
construct(y)
#> as.call(list(
#>   as.function(alist(x = , ... = , UseMethod("mean")), envir = .BaseNamespaceEnv),
#>   1
#> ))

construct(y, data = list(mean = mean))
#> as.call(list(mean, 1))

@moodymudskipper
Copy link
Collaborator Author

moodymudskipper commented Sep 2, 2022

Solved in 3b675b9 and improved secret and attribute handling with 593558b

@github-actions
Copy link
Contributor

github-actions bot commented Sep 4, 2023

This old thread has been automatically locked. If you think you have found something related to this, please open a new issue and link to this old issue if necessary.

@github-actions github-actions bot locked and limited conversation to collaborators Sep 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

1 participant