Skip to content
Back to formatted view

Raw Message

Message-ID: <8faf3d49-7fdd-4396-33ac-454e5ec0d3be@gmail.com>
Date: 2023-03-11T18:38:42Z
From: Duncan Murdoch
Subject: Multiple Assignment built into the R Interpreter?
In-Reply-To: <20230311195708.794032e8@Tarkus>

On 11/03/2023 11:57 a.m., Ivan Krylov wrote:
> On Sat, 11 Mar 2023 11:11:06 -0500
> Duncan Murdoch <murdoch.duncan at gmail.com> wrote:
> 
>> That's clear, but your proposal violates a very basic property of the
>> language, i.e. that all statements are expressions and have a value.
> 
> How about reframing this feature request from multiple assignment
> (which does go contrary to "everything has only one value, even if it's
> sometimes invisible(NULL)") to "structured binding" / "destructuring
> assignment" [*], which takes this single single value returned by the
> expression and subsets it subject to certain rules? It may be easier to
> make a decision on the semantics for destructuring assignment (e.g.
> languages which have this feature typically allow throwing unneeded
> parts of the return value away), and it doesn't seem to break as much
> of the rest of the language if implemented.
> 
> I see you've already mentioned it ("JavaScript-like"). I think it would
> fulfil Sebastian's requirements too, as long as it is considered "true
> assignment" by the rest of the language.
> 
> The hard part is to propose the actual grammar of the new feature (in
> terms of src/main/gram.y, preferably without introducing conflicts) and
> its semantics (including the corner cases, some of which you have
> already mentioned). I'm not sure I'm up to the task.
> 

If I were doing it, here's what I'd propose:

   '[' formlist ']' LEFT_ASSIGN expr
   '[' formlist ']' EQ_ASSIGN expr
   expr RIGHT_ASSIGN  '[' formlist ']'

where `formlist` has the syntax of the formals list for a function 
definition.  This would have the following semantics:

    {
      *tmp* <- expr

      # For arguments with no "default" expression,

      argname1 <- *tmp*[[1]]
      argname2 <- *tmp*[[2]]
      ...

      # For arguments with a default listed

      argname3 <- with(*tmp*, default3)
    }


The value of the whole thing would therefore be (invisibly) the value of 
the last item in the assignment.

Two examples:

   [A, B, C] <- expr   # assign the first three elements of expr to A, 
B, and C

   [A, B, C = a + b] <- expr  # assign the first two elements of expr
                              # to A and B,
                              # assign with(expr, a + b) to C.

Unfortunately, I don't think this could be done entirely by transforming 
the expression (which is the way |> was done), and that makes it a lot 
harder to write and to reason about.  E.g. what does this do?

   A <- 0
   [A, B = A + 10] <- list(1, A = 2)

According to the recipe above, I think it sets A to 1 and B to 12, but 
maybe a user would expect B to be 10 or 11.  And according to that 
recipe this is an error:

   [A, B = A + 10] <- c(1, A = 2)

which probably isn't what a user would expect, given that this is fine:

   [A, B] <- c(1, 2)

Duncan Murdoch