Стрелочные функции в PHP 7.4

Автор оригинала: Brent
  • Перевод


Стрелочные функции, также называемые короткими замыканиями (short closures), станут хорошим способом писать чистый код в PHP. Такая форма записи будет полезной при передаче замыканий в такие функции как array_map или array_filter.


Пример:


// Коллекция объектов Post
$posts = [/* … */];

$ids = array_map(fn($post) => $post->id, $posts);

Раньше нужно было писать так:


$ids = array_map(function ($post) {
    return $post->id;
}, $posts);

Кратко:


  • Доступны с PHP 7.4
  • Начинаются с ключевого слова fn
  • Могут иметь только одно выражение, также являющееся возвращаемым значением
  • Не поддерживается return
  • Type-hintihg поддерживается в аргументах и возвращаемых значениях

Строготипизированный способ написания примера выше:


$ids = array_map(fn(Post $post): int => $post->id, $posts);

Еще две важные вещи:


  • Поддерживается оператор переменного значения
  • Поддерживаются ссылки, как для аргументов, так и для возвращаемых значений

Если вы хотите вернуть значение по ссылке, используйте следующий синтаксис:


fn&($x) => $x

Стрелочные функции реализуют ту же функциональность, которую вы ожидаете от нормальных замыканий, только содержат в себе одно выражение.


Нет многострочности


Вы верно прочитали: короткие замыкания могут содержать только одно выражение. Это означает что вы не можете иметь несколько строк в них.


Аргументация такова: целью коротких замыканий является снижение многословности. fn, однозначно короче чем function во всех смыслах. Пропуск ключевых слов function и return ничего не меняет, но позволяет сделать код более читаемым.


Согласны ли вы с этим мнением? В то же время при наличии нового синтаксиса для однострочных функций, существует множество многострочных, которым тоже бы не помешал подобный апгрейд.


Надеюсь, в будущем, появится RFC с коротким объявлениим и многострочных функций, но пока это только мои мечты.


Переменные из внешней области видимости


Еще одним существенным различием между короткими и нормальными замыканиями является то, что первые не требуют использования ключевого слова use для получения доступа к данным из внешней области видимости.


$modifier = 5;

array_map(fn($x) => $x * $modifier, $numbers);

Важно отметить, что вы не можете изменять эти переменные. Значения связаны по значению, а не по ссылке. Это означает, что вы можете изменять $modifier внутри короткого замыкания, но это не повлияет на переменную $modifier находящуюся вне.


Единственное исключение — ключевое слово $this, которое будет работать точно также, как и в нормальном варианте:


array_map(fn($x) => $x * $this->modifier, $numbers);

Будущие возможности


Я уже упоминал идею с многострочными короткими замыканиями выше. Еще одно полезное предложение — позволить использовать короткий синтаксис в классах, например для геттеров и сеттеров:


class Post {
    private $title;

    fn getTitle() => $this->title;
}

В целом, стрелочные функции это очень хорошая фича, хотя есть еще область для улучшения.


Есть ли у вас какие-либо мысли по этому поводу?

Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 19

    +2
    $posts = [/* … */];

    $ids = array_map(fn($post) => $post->id, $posts);
    Это, конечно, удобно, но гораздо удобнее была бы возможность делать вот так:
    $posts = [/* … */];

    $ids = $posts->map(fn($post) => $post->id);
    Увы, это вряд ли когда-либо завезут в PHP (да-да, я знаю что существуют библиотеки реализующие похожий синтаксис).

    Ещё не очень понятно нахрена оператор fn перед стрелочной функцией. Ограничения парсера?
      +2
      нахрена оператор fn перед стрелочной функцией
      $array = [
      $a => $a + $b,
      $x => $x * $y,
      ];

      Это массив arrow functions или map?


      yield $foo => $bar;

      Это key-value yield или arrow function yield?


      Понятно, что можно ввести приоритеты и расставить скобочки — но это усложнение языка и способ наделать ошибок на пустом месте.


      Я, если что, был за \($x) => ..., как в Haskell. Но с fn тоже неплохо.

        0
        Map конечно. А вот это
        $array = [
        ($a) => $a + $b,
        ($x) => $x * $y,
        ];
        массив стрелочных функций. ИМХО, так было бы логичней, хотя и потребовало бы оборачивать в круглые скобки даже один аргумент.

        Upd. а, сейчас посмотрел — и так надо всегда заворачивать в круглые скобки, делать $x => $x + $y и так, вроде, нельзя. Так что ноль потерь.
          –1

          А если мне надо мапу вида ($a + $x) * $y => $a + $b? :-)


          Понятно, что реализуемо, но слишком замороченно, не надо из PHP делать C++.

            0
            не надо из PHP делать C++
            Правильно, поэтому предлагают запилить Р++!
              +3

              … и все единогласно проголосовали против.

                0
                ¯\_(ツ)_/¯
            +2
            Это я бы тоже распарсил как обычный массив. А если нужен массив стрелочных функций, пришлось бы написать так:

            $array = [
              ($a => $a + $b),
              ($x => $x * $y),
            ];
            // то же, с yield:
            yield ($foo => $bar);
            
          +1
          welocme to JS =)
            +1
            Так я 50/50 и пишу, то на PHP то на JS) Оттуда и грусть, потому что знаю как это может быть удобно
          +1

          Шёл 2019 год. В PHP добавили короткие лямбды.


          Ни в коем случае не умаляю работы над языком, но запоздание фичи довольно-таки велико.

            0
            Там, вроде, очень много времени заняли споры по поводу синтаксиса для этих коротких лямбд. Были и другие предложения, например синтаксис $x ~> $x + 1.
              +6

              Вялые/пьяные функции

              –1
              Ещё лет через 5 придумают полноценные короткие лямбды, многострочные.
              +2
              class Post {
                  private $title;
                  fn getTitle() => $this->title;
              }
              

              Какой ужас. Давно же придумали лаконичный и однозначный синтаксис:

              class Post {
                  private $title { get; protected set; }
              }
              
                0
                Раньше нужно было писать так:
                $ids = array_map(function ($post) {
                    return $post->id;
                }, $posts);

                До декабря 2015-го действительно так нужно было писать, зато начиная с php 7 можно писать гораздо проще:
                
                array_column($collection, $property)
                

                Начиная с 7.0 функция array_column в качестве входного параметра может принимать не только массив, но и коллекцию объектов. демо
                Мне кажется что это более чистый и читабельный вариант, чем даже стрелочная функция.
                  0
                  Все верно, ваш вариант проще в конкретном примере, но если нужно что-то сложнее?
                  $vat = 0.2;
                  $total = array_sum(
                      array_map(
                          function (Order $order) use ($vat) {
                              return $order->price + $order->price * $vat;
                          },
                          $orders
                      )
                  );
                  

                  $vat = 0.2;
                  $total = array_sum(
                      array_map(
                          fn (Order $order): float => $order->price + $order->price * $vat,
                          $orders
                      )
                  );
                  


                    0
                    Я и не говорю, что array_column является решением на все случаи жизни, но единственную свою задачу получения поля из коллекции решает хорошо.
                    Если нужно вычислить какое-то выражение от поля, разумеется придется прибегнуть или к array_map или к аналогичному решению. Хорошо хоть проход в цикле по массиву с копированием значений во временную переменную в коде можно встретить все реже.
                    Хорошо, что есть возможность передать в array_map анонимную функцию (а теперь и стрелочную функцию). Но не стоит злоупотреблять анонимными функциями. Именованные функции несут важную миссию- они добавляют контекст и семантику коду, и вам не требуется лезть в реализацию функции, чтобы понять что она делает.

                    Совершенно непонятно что такое:
                    $order->price + $order->price * $vat;

                    А так понятно:
                    $order->getPriceWithTaxes();

                    Или с использованием функции (если точности чисел с плавающей точкой достаточно):
                    function getPriceWithTaxes(float $price, float $taxes): float;
                      0
                      На то оно и программирование, что существует множество вариантов.
                      Вот пример из оф. документации

                      Example #1 Anonymous function example

                      <?php
                      echo preg_replace_callback('~-([a-z])~', function ($match) {
                          return strtoupper($match[1]);
                      }, 'hello-world');
                      // outputs helloWorld
                      ?>
                      

                      Который превращается в:

                      <?php
                      echo preg_replace_callback('~-([a-z])~', fn ($match) => strtoupper($match[1]), 'hello-world');
                      // outputs helloWorld
                      ?>
                      

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое