My Testing Was Hurt By a List With No Names

I spied a strange thing with my innocent eye. I was testing spread_to_list, the function I wrote about in “How Best to Convert a Names-Values Tibble to a Named List?”. One of my tests passed it a zero-row tibble, expecting that the result would be a zero-element list:

test_that( "Test on zero-element tibble", {
  t <- tribble( ~a, ~b )
  l <- spread_to_list( t )
  expect_that( length( l ), equals( 0 ) )
  expect_that( l, is_identical_to( list() ) )
})
But it wasn’t. Or at least, it was, but with an unsuspected excrescence.

The package I used to run that test is Hadley Wickham’s testthat, explained in his 2011 article “testthat: Get Started with Testing”. Even if you don’t know it, you can predict from his well-chosen function names what two things I expected. The first thing, that the result be length zero, came true. The second thing, that it be an empty list, didn’t.

Output from the test gave me a hint:

Error: Test failed: 'Test on zero-element tibble'
* `l` not identical to list().
Attributes: < Modes: list, NULL >
Attributes: < Lengths: 1, 0 >
Attributes: < names for target but not for current >
Attributes: < current is not list-like >

Displaying l and a list() told me the rest:

> l
named list()
> list()
list()
R evidently thinks a named list is not identical to a list, even when the set of names is empty. I don’t know whether this is reasonable or not. An empty set is different from no set. On the other hand, a dictionary which just happens to have no definitions in it is still a dictionary. The R manual’s section on object attribute lists implies that a list’s names are an attribute. Let’s see how that behaves:
> attributes(l)
$names
character(0)

> attributes(list())
NULL
So a list can either have no names and no box to put them in, or the box with nothing inside.

Leave a Reply

Your email address will not be published. Required fields are marked *