dgkf / R

An experimental reimagining of R
https://dgkf.github.io/R
GNU General Public License v3.0
136 stars 5 forks source link

Suggestion: syntax for non-standard string literals #79

Open wurli opened 9 months ago

wurli commented 9 months ago

One could define a function to operate on a single string like so:

`g"` <- function(x) {
  glue::glue(x, .envir = parent.frame())
}

This would basically give you something very similar to Python's f-strings:

g"1 + 1 = {1 + 1}"
#> [1] "1 + 1 = 2"

Obviously, the {glue} package isn't available in this version of R, but you get the idea 😄

This idea comes from Julia, which lets you define non-standard string literals in the same way: https://docs.julialang.org/en/v1/manual/metaprogramming/#meta-non-standard-string-literals.

dgkf commented 9 months ago

I love julia's string literals!

I have a couple thoughts on the exact implementation, but I think supporting something like this would be really nice as a general concept.

I have mixed feelings about R's reliance on meaningful syntax in function names (fn<-, %fn%, fn.class, and now fn"). Julia uses this too with its fn_str convention, but gets away from the .class syntax with its dispatch system. An alternative would be to have a generic similar to infix operators.

I've often felt it would be nice to incorporate a juxtapose generic. When two parsed nodes are butted up against eachother, it would be equivalent to a juxtapose call, i.e. g"string" gets parsed as juxtapose(g, "string"), which then could repurpose the dispatch system to handle this case, and many more!

Then (using R's S3 dispatch style for the sake of example), to implement this we might do:

class(glue::glue) <- c("glue_fmt_function", "function")
g <- glue::glue

juxtapose.glue_fmt_function <- function(e1, e2) {
  e1(e2)
}

Or to move entirely away from meaningful syntax in function names dispatch might be articulated more like it is in Julia with typed parameters:

juxtapose <- function(e1: glue_fmt_function, e2) {
  e1(e2)
}
dgkf commented 9 months ago

Just as a motivating example, allowing dispatch on this type of juxtapose generic would allow use cases where users generate a formatter:

fmt <- my_code_style(whitespace = 3L, always_backtick_names = TRUE)
fmt"a + b"
# [1] `a`   +   `b`

This is a silly example with a silly code style, but you can imagine situations when someone might have more reasonable settings for formatters. Perhaps things like json-prettyfication or error message formatting style.

wurli commented 9 months ago

The juxtapose idea is nice - I also have mixed feelings about syntax in function names. I'm not sure if it could potentially cause any readability issues, e.g. code that changes function with the addition/omission of a single space. My gut feeling is that it would, but I can't think of any cases where that could happen, so maybe it would be okay. Maybe interference with infix operators could somehow be an issue?

I think maybe a good approach would be to decide sooner rather than later whether syntax in function names is something this project will keep. If it is kept, I think it should probably be used in favour of juxtapose. But, I think it would be infinitely cooler to take the plunge and cook up an alternative, unified syntax for defining generics, infix operators, assignment operators etc. In this case, something like juxtapose could be a really nice addition and should hopefully slot nicely into that system.

dgkf commented 9 months ago

I share your concerns. Introducing meaningful whitespace is a language decision that shouldn't be taken lightly.

There's also an option for a middle ground. Maybe a totally generic juxtapose function is a bit overkill, but we still want some format generic that accepts a formatter and a string. Unlike juxtapose, which might apply to any two juxtaposed syntax nodes, maybe something like format would only be called internally when a name precedes a string literal.

This would still let us use dispatch to introduce new methods without the bespoke function declaration syntax while achieving a very similar behavior. Because the design stands to benefit so much from generics, I'd probably hold off on this until we have the generics/dispatch built up.

I do think something like this is tremendously valuable for any analytic language. glue is a great example, but I especially like julia's SQL and regex format strings.