Skip to content

For *_join functions, requesting handling when argument "on" is specified in place of "by" #6228

Closed

Description

@baogorek

As a heavy SQL user, I'm constantly using argument "on" instead of "by" in inner_join and left_join. These functions do echo what they're actually joining on, but it's easy to miss. I feel like these functions should stop you in your tracks if you try to use "on" or throw a warning and make the substitution.

Bit of a contrived example below, but in both cases it's clear the user is intending to join on "id" and not c("id", "x")

library(dplyr)
df <- data.frame(id = c(1,2), x = c(3.4, 2.6))
df2 <- data.frame(id = c(1,2), x = c(3.4, 2.7), z = c(4.4, 5.4))

df %>% inner_join(df2, on="id")
df %>% inner_join(df2, by="id")

Activity

  1. DavisVaughan commented on Apr 7, 2022

    @DavisVaughan
    Member

    Minimal reprex of the general idea that our join functions swallow ... silently

    library(dplyr)
    
    left_join(
      band_instruments,
      band_members,
      by = "name",
      NONEXISTENT = "ARG"
    )
    #> # A tibble: 3 × 3
    #>   name  plays  band   
    #>   <chr> <chr>  <chr>  
    #> 1 John  guitar Beatles
    #> 2 Paul  bass   Beatles
    #> 3 Keith guitar <NA>

    @hadley do you think it would be reasonable for the data frame methods to call check_dots_empty()? I have a feeling putting any checks in the generic at this point is going to cause issues, but it feels safe and useful to put a check in the data frame method.

  2. hadley commented on Apr 8, 2022

    @hadley
    Member

    @DavisVaughan can we use check_dots_used() in the generic?

  3. DavisVaughan commented on Apr 8, 2022

    @DavisVaughan
    Member

    I'll give that a try and see if revdeps flags anything. That seems reasonable.

    Details

    Convincing myself that check_dots_used() works as we want:

    fn <- function(x, ...) {
      rlang::check_dots_used()
      UseMethod("fn")
    }
    
    fn.default <- function(x, ...) {
      list(x)
    }
    
    fn.double <- function(x, ..., option = NULL) {
      list(x, option)
    }
    
    # error because `option` isnt a valid argument to that method
    fn(1L, option = 2)
    #> Error:
    #> ! Arguments in `...` must be used.
    #> x Problematic argument:
    #> • option = 2
    
    # fine
    fn(1, option = 2)
    #> [[1]]
    #> [1] 1
    #> 
    #> [[2]]
    #> [1] 2
    
    # typo
    fn(1, options = 2)
    #> Error:
    #> ! Arguments in `...` must be used.
    #> x Problematic argument:
    #> • options = 2

    VS check_dots_empty() which is nice in the method but not nice in the generic

    fn <- function(x, ...) {
      rlang::check_dots_empty()
      UseMethod("fn")
    }
    
    fn.double <- function(x, ..., option = NULL) {
      list(x, option)
    }
    
    # not good for us, we want to allow this
    fn(1, option = 2)
    #> Error in `fn()`:
    #> ! `...` must be empty.
    #> x Problematic argument:
    #> • option = 2

    check_dots_used() does have this slightly unfortunate behavior though

    fn <- function(x, ...) {
      rlang::check_dots_used()
      UseMethod("fn")
    }
    
    fn.integer <- function(x, ..., option = NULL) {
      list(x, option)
    }
    
    fn.double <- function(x, ..., option = NULL) {
      list(x)
    }
    
    # Actually evaluates the `option` promise so no issues
    fn(1L, option = 2)
    #> [[1]]
    #> [1] 1
    #> 
    #> [[2]]
    #> [1] 2
    
    # Doesn't evaluate the `option` promise, but we do have an `option` argument.
    # Since the promise isn't evaluated it looks "unused"
    fn(1, option = 2)
    #> Error:
    #> ! Arguments in `...` must be used.
    #> x Problematic argument:
    #> • option = 2
  4. added
    featurea feature request or enhancement
    tables 🧮joins and set operations
    on Apr 15, 2022
  5. added a commit that references this issue on Jul 22, 2022
    4acc8ec
  6. added a commit that references this issue on Jul 26, 2022
    17c7a5d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    featurea feature request or enhancementtables 🧮joins and set operations

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions