В Perl любая функция может определять, в каком контексте она вызвана: скалярном, списочном или void. Оператор wantarray используется для определения контекста.
Пример:
sub foo { if (wantarray) { return (1, 2, 3); # Списочный } else { return 42; # Скалярный } } my @a = foo(); # (1, 2, 3) my $b = foo(); # 42 foo(); # Void context
Особенность: если не использовать wantarray правильно, можно возвращать неожиданные значения.
Что вернёт функция, если в зависимости от контекста она возвращает массив и скаляр, а вызывается в void-контексте?
Ответ: В void-контексте возвращаемое значение игнорируется, однако сам код исполнения ветвления по wantarray остается. Поэтому нужно предусмотреть отдельную обработку, если функция выполняет побочные действия. Например:
sub noisy { if (wantarray) { print "Called in list context"; return (1,2,3); } elsif (defined wantarray) { print "Called in scalar"; return 42; } else { print "Void!"; return; } }
История 1
На проекте один инженер забывал об отличиях контекста. Функция, возвращавшая список имен файлов, иногда "отдавала" только количество файлов (скалярный контекст) вместо самих имён — это приводило к потере данных при конкатенации с другими результатами.
История 2
При разработке API для парсинга заголовков функции возвращали список только если явно был массив слева (
my @headers = parse_headers()), а если вызывали какif (parse_headers()), возвращался только первый заголовок. Баг нашли далеко не сразу, так как обработка выглядела логично — но поведение отличалось в разных частях проекта.
История 3
Функция для поиска совпадений в файле возвращала разное количество результатов в зависимости от контекста. В результате при передаче её в map или grep происходили неожиданные ошибки: map ожидал список, но получал undef — а в скалярном контексте считалось только число найденных совпадений. Проблема обнаружилась только после нескольких релизов.