Case 331
Returns a copy, the second caller assumed in place — the call site is identical
returns_a_copy_second_caller_assumed_in_place.eml calls one cleaning helper two ways over the same input and reports what each caller ends up holding.
ok: true — round-trip fixpoint reached (python1 == python2)updated 2026-08-10
EML
eml# Self-authored for the EML case corpus (no external origin). A helper that
# returns a cleaned copy, and a second caller that assumed it cleaned in place.
#
# The helper's signature does not say which it is. Its tests do not either -
# they check the RETURN VALUE, which is correct and which passes whether or not
# the argument was also modified. The only place the distinction is recorded is
# the call site, and the two call sites disagree.
#
# Caller A wrote `clean(rows) => rows` and gets clean data. Caller B wrote
# `clean(rows)` on a line of its own, the way you call something that works in
# place, and gets its original data - with no error, no warning, and a helper
# that ran and did its job.
#
# `=>` binds a name to a value, so both callers hold a real list either way.
# Nothing about the binding tells them whether the list they hold is the one
# the helper worked on. That question is not answerable from the call site,
# which is why it gets answered by habit.
#
# The measurement runs both call styles over the same input and reports what
# each caller ends up holding, plus whether the helper's own tests could have
# distinguished them.
def clean(rows):
[] => out
for r in rows:
if r < 0:
out + [0] => out
else:
out + [r] => out
return out
def clean_in_place(rows):
for i in [0:len(rows) - 1]:
if rows[i] < 0:
0 => rows[i]
return rows
def negatives_left(rows):
0 => n
for r in rows:
if r < 0:
n + 1 => n
return n
[4, 0 - 2, 7, 0 - 9, 1] => source
# caller A: uses the return value
[4, 0 - 2, 7, 0 - 9, 1] => a_rows
clean(a_rows) => a_rows
# caller B: calls it the way you call something that works in place
[4, 0 - 2, 7, 0 - 9, 1] => b_rows
clean(b_rows)
"the same helper, two call styles, same input" ^0
" source : " + repr(source) ^0
" caller A (uses return value) : " + repr(a_rows) + " negatives left " + str(negatives_left(a_rows)) ^0
" caller B (assumed in place) : " + repr(b_rows) + " negatives left " + str(negatives_left(b_rows)) ^0
"" ^0
# ---- the helper's own tests ----
"the helper's own fixtures, which check the return value" ^0
[[[0 - 1], [0]], [[3], [3]], [[0 - 5, 2], [0, 2]], [[], []]] => fixtures
0 => failing
for f in fixtures:
clean(f[0]) => got
if repr(got) != repr(f[1]):
failing + 1 => failing
" fixtures failing: " + str(failing) + " of " + str(len(fixtures)) ^0
"" ^0
"could those fixtures have caught caller B's mistake?" ^0
0 => fixtures_that_notice
for f in fixtures:
[] => arg
for v in f[0]:
arg + [v] => arg
clean(arg)
if repr(arg) != repr(f[0]):
fixtures_that_notice + 1 => fixtures_that_notice
" fixtures whose ARGUMENT changed after the call: " + str(fixtures_that_notice) + " of " + str(len(fixtures)) ^0
" a return-value test cannot see the difference, because there is none to see" ^0
"" ^0
# ---- the in-place version, for contrast ----
[4, 0 - 2, 7, 0 - 9, 1] => c_rows
clean_in_place(c_rows)
"an in-place helper called the same way caller B called the copying one" ^0
" after clean_in_place(c_rows) : " + repr(c_rows) + " negatives left " + str(negatives_left(c_rows)) ^0
"" ^0
"Both helpers pass a return-value test. Both are called with one line. Only" ^0
"one of them leaves the caller's data clean, and the call site is identical." ^0Python (deterministic transpilation)
pythondef clean(rows):
out = []
for r in rows:
if r < 0:
out = out + [0]
else:
out = out + [r]
return out
def clean_in_place(rows):
for i in range(0, len(rows)):
if rows[i] < 0:
rows[i] = 0
return rows
def negatives_left(rows):
n = 0
for r in rows:
if r < 0:
n = n + 1
return n
source = [4, 0 - 2, 7, 0 - 9, 1]
a_rows = [4, 0 - 2, 7, 0 - 9, 1]
a_rows = clean(a_rows)
b_rows = [4, 0 - 2, 7, 0 - 9, 1]
clean(b_rows)
print("the same helper, two call styles, same input")
print(" source : " + repr(source))
print(" caller A (uses return value) : " + repr(a_rows) + " negatives left " + str(negatives_left(a_rows)))
print(" caller B (assumed in place) : " + repr(b_rows) + " negatives left " + str(negatives_left(b_rows)))
print("")
print("the helper's own fixtures, which check the return value")
fixtures = [[[0 - 1], [0]], [[3], [3]], [[0 - 5, 2], [0, 2]], [[], []]]
failing = 0
for f in fixtures:
got = clean(f[0])
if repr(got) != repr(f[1]):
failing = failing + 1
print(" fixtures failing: " + str(failing) + " of " + str(len(fixtures)))
print("")
print("could those fixtures have caught caller B's mistake?")
fixtures_that_notice = 0
for f in fixtures:
arg = []
for v in f[0]:
arg = arg + [v]
clean(arg)
if repr(arg) != repr(f[0]):
fixtures_that_notice = fixtures_that_notice + 1
print(" fixtures whose ARGUMENT changed after the call: " + str(fixtures_that_notice) + " of " + str(len(fixtures)))
print(" a return-value test cannot see the difference, because there is none to see")
print("")
c_rows = [4, 0 - 2, 7, 0 - 9, 1]
clean_in_place(c_rows)
print("an in-place helper called the same way caller B called the copying one")
print(" after clean_in_place(c_rows) : " + repr(c_rows) + " negatives left " + str(negatives_left(c_rows)))
print("")
print("Both helpers pass a return-value test. Both are called with one line. Only")
print("one of them leaves the caller's data clean, and the call site is identical.")stdout (executed)
textthe same helper, two call styles, same input
source : [4, -2, 7, -9, 1]
caller A (uses return value) : [4, 0, 7, 0, 1] negatives left 0
caller B (assumed in place) : [4, -2, 7, -9, 1] negatives left 2
the helper's own fixtures, which check the return value
fixtures failing: 0 of 4
could those fixtures have caught caller B's mistake?
fixtures whose ARGUMENT changed after the call: 0 of 4
a return-value test cannot see the difference, because there is none to see
an in-place helper called the same way caller B called the copying one
after clean_in_place(c_rows) : [4, 0, 7, 0, 1] negatives left 0
Both helpers pass a return-value test. Both are called with one line. Only
one of them leaves the caller's data clean, and the call site is identical.Trace event types
eml:run:starteml:defeml:assigneml:calleml:returneml:outputeml:run:done