Хороший… Плохой… Главное — у кого ружьё!
6,4
рейтинг
26 января 2013 в 15:51

Разработка → Функциональное программирование на PHP перевод

PHP всегда был простым, процедурным языком программирования, черпавшим свое вдохновение из C и Perl. В PHP 5 появилась правильная объектная модель, но о ней вы уже все знаете. А вот в PHP 5.3 появились замыкания (closure), которые были серьезно улучшены в версии 5.4 (подсказка: $this теперь доступен по умолчанию).

Что же это все-таки такое — функциональное программирование?

Прошло уже несколько лет с тех пор, как я начал использовать функциональные элементы в своем исходном коде, но я все еще не готов дать прямого и точного ответа на этот вопрос. И все же, несмотря на то, что у меня пока что нет четкого определения – я с уверенностью могу сказать, когда передо мной пример функционального программирования, а когда — нет. Поэтому я попробую зайти немного с другой стороны: функциональные программы обычно не прибегают к изменению состояний, а используют чистые функции. Эти чистые функции принимают значение и возвращают значение без изменения своего входного аргумента. Противоположный пример – это типичный сеттер в объектно-ориентированном контексте.

Типичный функциональный язык программирования поддерживает также функции высокого порядка – это функции, которые принимают в качестве аргументов или возвращают другие функции. Большинство из них поддерживает такие вещи, как карринг (currying) и частичное применение функции (partial function application). Также в языках функционального программирования можно встретить тщательно продуманную систему типов, которые используют option type для предотвращения появления нулевых указателей, которые стали обычным делом для императивных или объектно-ориентированных языков программирования.

Функциональное программирование обладает несколькими соблазнительными свойствами: отсутствие возни с состояниями делает параллелизм проще (но не простым – параллелизм никогда не бывает простым), фокусировка на функции — на минимальной единице кода, который можно было бы использовать снова – может привести к интересным вещам, связанным с их повторным использованием; требование к функциям быть определенными это отличная идея для создания стабильных программ.

Что может предложить PHP?

PHP не является «настоящим» или «чистым» функциональным языком. Он далек от этого. Здесь нет надлежащей системы типов, "крутые пацаны" катаются со смеху от нашего экзотического синтаксиса для замыканий, а еще тут есть функция array_walk(), которая на первый взгляд выглядит функциональной, но позволяет изменение состояний.

Тем не менее, здесь есть несколько интересных «строительных блоков» для целей функционального программирования. Для начала, возьмем call_user_func, call_user_func_array и $callable(). call_user_func принимает callback-функцию и список аргументов, после чего вызывает этот callback с переданными аргументами. call_user_func_array делает то же самое, за исключением того, что она принимает массив аргументов. Это очень похоже на fn.call() и fn.apply() в JavaScript (без передачи области видимости). Гораздо менее известная, но отличная функция в PHP 5.4 это возможность вызывать функции. callable это мета-тип в PHP (то есть состоящий из нескольких вложенных типов): callable может быть строкой для вызова простых функций, массивом из <string,string> для вызова статичных методов и массивом из <object,string> для вызова методов объекта, экземпляра Closure или чего угодно, осуществляющего магический метод __invoke(), также известный как Функтор. Это выглядит примерно следующим образом:

$print = 'printf';
$print("Hello %s\n", 'World');

В PHP 5.4 появился новый тип “callable”, который позволяет простой доступ к мета-типу callable.
PHP в том числе поддерживает анонимные функции. Как упоминалось ранее, сообщество Haskell от души смеется над этим фактом, но главного все равно не отнять — мы наконец-то их получили. Да и шутки были вполне ожидаемы, потому что синтаксис выражений стал очень тяжелым. Возьмем простой пример на Python.

map(lambda v: v * 2, [1, 2, 3])

Симпатично, теперь взглянем на тот же код для Ruby:

[1, 2, 3].map{|x| x * 2}

Тоже неплохо, хоть нам и пришлось использовать блок и нестрогое лямбда-выражение. У Ruby тоже есть лямбда-выражения, но List.maphappens принимает блок, а не функцию. Перейдем к Scala:

List(1, 2, 3).map((x: Int) => x * 2)

Как видно из примеров, для строго типизированного языка программирования синтаксис всегда остается довольно компактен. Перепишем наш пример на PHP:

array_map(function ($x) {return $x * 2;}, [1, 2, 3]);

Ключевое слово function и отсутствие неявного return заставляют код выглядеть немного громоздким. Но, тем не менее, он работает. Еще один «строительный блок» в копилку для функционального программирования.
Кстати, array_map дает неплохой старт, но стоит учесть, что есть еще и array_reduce; вот вам еще две важные функции.

Функциональный пример из реального мира

Давайте напишем простую программу, которая подсчитывает общую цену корзины покупок:

$cart = [
    [
        'name'     => 'Item 1',
        'quantity' => 10,
        'price'    => 9.99,
    ],
    [
        'name'     => 'Item 2',
        'quantity' => 3,
        'price'    => 5.99,
    ]
];
 
function calculate_totals(array $cart, $vatPercentage)
{
    $totals = [
        'gross' => 0,
        'tax'   => 0,
        'net'   => 0,
    ];
 
    foreach ($cart as $position) {
        $sum = $position['price'] * $position['quantity'];
        $tax = $sum / (100 + $vatPercentage) * $vatPercentage;
        $totals['gross'] += $sum
        $totals['tax'] += $tax
        $totals['net'] += $sum - $tax; 
    }
 
    return $totals;
}
 
calculate_totals($cart, 19);

Да, это очень простой пример, который будет работать только для одного магазина. Но в нем используются не слишком сложные вычисления, и благодаря этому мы можем легко его переделывать, приводя к более функциональному стилю.

Давайте начнем с использования функций высшего порядка:

$cart = [
    [
        'name'     => 'Item 1',
        'quantity' => 10,
        'price'    => 9.99,
    ],
    [
        'name'     => 'Item 2',
        'quantity' => 3,
        'price'    => 5.99,
    ]
];
 
function calculate_totals(array $cart, $vatPercentage)
{
   $cartWithAmounts = array_map(
       function (array $position) use ($vatPercentage) {
           $sum = $position['price'] * $position['quantity'];
           $position['gross'] = $sum;
           $position['tax'] = $sum / (100 + $vatPercentage) * $vatPercentage;
           $position['net'] = $sum - $position['tax'];
           return $position;
       },
       $cart
   );
 
   return array_reduce(
       $cartWithAmounts,
       function ($totals, $position) {
           $totals['gross'] += $position['gross'];
           $totals['net'] += $position['net'];
           $totals['tax'] += $position['tax'];
           return $totals;
       },
       [
           'gross' => 0,
           'tax'   => 0,
           'net'   => 0,
       ]
   );
}
 
calculate_totals($cart, 19);

Теперь изменения состояний не происходит, даже внутри самой функции. array_map() возвращает новый массив из списка позиций в корзине с весом, налогом и стоимостью, а функция array_reduce собирает вместе массив итоговой суммы. Можем ли мы пойти дальше? Можем ли мы сделать программу еще проще?

А что, если мы разобьем программу на части еще меньше и посмотрим, что она делает на самом деле:
  • Суммирует элемент массива, умноженный на другой элемент
  • Забирает часть процентов от этой суммы
  • Считает разницу между процентами и суммой

Теперь нам потребуется маленький помощник. Этим маленьким помощником нам станет functional-php, небольшая библиотека функциональных примитивов, которую я разрабатываю уже несколько лет. Для начала, тут есть Functional\pluck(), которая делает то же самое, что и _.pluck() из underscore.js. Другая полезная функция оттуда — это Functional\zip(). Она «сжимает» вместе два списка, опционально используя callback-функцию. Functional\sum() суммирует элементы списка.

use Functional as F;
$cart = [
    [
        'name'     => 'Item 1',
        'quantity' => 10,
        'price'    => 9.99,
    ],
    [
        'name'     => 'Item 2',
        'quantity' => 3,
        'price'    => 5.99,
    ]
];
 
function calculate_totals(array $cart, $vatPercentage)
{
    $gross = F\sum(
        F\zip(
            F\pluck($cart, 'price'),
            F\pluck($cart, 'quantity'),
            function($price, $quantity) {
                return $price * $quantity;
            }
        )
    );
    $tax = $gross / (100 + $vatPercentage) * $vatPercentage;
 
    return [
        'gross' => $gross,
        'tax'   => $tax,
        'net'   => $gross - $tax,
    ];
}
 
calculate_totals($cart, 19);

Сразу возникает отличный контраргумент: правда ли, что пример стал проще для чтения? С первого взгляда — определенно нет, но со второго и дальше — вы привыкните. Лично у меня ушло какое-то время на то, чтобы привыкнуть к синтаксису Scala; сколько-то времени заняло изучение ООП и еще немало ушло на понимание функционального программирования. Это самая совершенная форма, в которую можно превратить исходный пример? Нет. Но при помощи этого кода вы увидели, насколько сильно меняется ваш подход к нему, когда вы мыслите в рамках применения функций к структурам данных, а не использования выражений вроде foreach для обработки структур данных.

Что еще можно сделать?

Вы когда-нибудь сталкивались с исключениями нулевого указателя (null pointer exceptions)? Существует такая вещь, как php-option, которая предоставляет нам реализацию полиморфического типа «возможно» (maybe) при помощи PHP-объекта.

Этому есть частичное применение: она превращает функцию, которая принимает n параметров, в функцию, которая принимает <n параметров. Чем это может быть полезно? Возьмем извлечение первого символа из списка строк.
Скучный путь:

$list = ['foo', 'bar', 'baz'];
$firstChars = [];
foreach ($list as $str)  {
    $firstChars[] = substr($str, 0, 1);
}

Функциональный путь без PFA (частичного применения функций):

array_map(function ($str) {return substr($str, 0, 1);}, ['foo', 'bar', 'baz']);


Путь с PFA и с использованием reactphp/curry (моя любимая реализация карринга для PHP):

use React\Curry;
array_map(Curry\bind('substr', Curry\…(), 0, 1), ['foo', 'bar', 'baz']);

Да. … (HORIZONTAL ELLIPSIS, U+2026) это корректное имя функции в PHP. Но если оно вам по какой-то причине не сильно приглянулось, можете использовать вместо него useCurry\placeholder().

Вот и все

Функциональное программирование это очень увлекательная тема; если бы меня спросили, что стало самой важной выученной мною вещью за последние годы, то я бы сразу ответил — знакомство с функциональными парадигмами. Оно настолько не похоже на все, что вы пробовали до этого, что ваш мозг будет болеть. Ну, в хорошем смысле.

И напоследок: почитайте «Функциональное программирование в реальном мире» (Real World Functional Programming). Там полным-полно хороших советов и примеров использования на практике.

Жду ваших замечаний и поправок к статье в личных сообщениях.
Перевод: Lars Strojny
Владимир Маслов @HotWaterMusic
карма
166,7
рейтинг 6,4
Хороший… Плохой… Главное — у кого ружьё!
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (82)

  • +8
    А не проще язык поменять? В PHP всё выглядит крайне многословным и даже запутанным.
    • –6
      Предложи фейсбуку поменять язык.
      • +1
        Речь о разработчике, а не о проекте.

        В PHP на самом деле выгоднее всего придерживаться процедурного стиля, да и в обычном объектно-ориентированном стиле программировать можно.

        А если очень нравится функциональный стиль, это повод сменить и язык, и проект.
        • +4
          >> В PHP на самом деле выгоднее всего придерживаться процедурного стиля
          С чего бы?
          • +3
            Вы цитируете лишь часть фразы.

            Обычное ООП лишь немногим хуже процедурного стиля. Мне не нравится позднее развитие PHP. Очень много нового вводится с безобразным синтаксисом.

            Но это, понятно, лишь личное мнение.
            • +4
              Что значит не проще ли язык поменять? Если я регулярно покупаю любимую газету и в ней появляется раздел с анекдотами, я читаю и радуюсь этому, вовсе не означает, что мне проще отдельно покупать сборник анекдотов.
        • +4
          Вы не учитываете факт популярности языка. Это очень важный момент. Он влияет на цену на рынке, на количество готовых библиотек (yf выгрузку с базы TecDoc есть готовые скрипты, на интеграцию с любым сервисом всегда есть пример на PHP). Не учитываете скорость на гугление ошибки. Потому фраза я буду кодить на этом языке потому что мне нравится синтаксис хорошо себя показывает только в вакууме. Или если вы одарен. Но тогда все пойдет.
          • +1
            Откройте глаза, php не единственный язык, применяемый сейчас на планете. Ну сколько можно уже этот бред множить, дескать на других языках работы нет, разработчиков нет.
            • +2
              Тут вопрос сколько работы есть и сколько есть разработчиков.

              А разработчику сменить язык на тот, который ему нравится без потерь в зарплате далеко не всегда представляется возможным. Грубо говоря, смена языка означает переход с Senior на Junior, даже если «предметная область» одна (веб-разработка).
            • +1
              Речи нет про использование языка. Вы конечно можете перейти на Ruby. И делать те же задачи. Но (к примеру) вам нужно сделать выгрузку в xls и встает проблема. На php это решается за 20 минут (найти библиотеку + разобраться в ней). На руби к примеру может занять неделю. Или нужно сделать выгрузку из TecDoc. Да даже интеграцию с платежной системой нужно изучить сначала на php и потому переписать на руби.
              Вам как разработчику плевать на эти причины. Вы с радостью будете клепать выгрузку в ексель хоть месяц получая за это свои 10$ в час. А мне как работодателю это совершенно не нравится. Именно по этому мне нужны PHP программисты.
              Я разделил мнения на 2 типа что бы лучше показать различные требования. Работа идет ради работы или ради результата.
              • 0
                Простите, даже не смешно. Вобоще не знаю Руби, вбил в гугл «ruby write xls», открыл первый же результат. Прошел по ссылке, на странице библиотеки запустил одну команду из параграфа Installing. Скопировал код из ответа на stackoverflow, запустил. Получил «uninitialized constant Spreadsheet (NameError)». Открыл еще раз страницу библотеки, из первого же примера скопировал первую строчку «require 'roo'», запустил еще раз. Получил «undefined local variable or method `input' for main:Object (NameError)», посмотрел код, увидел, как используется input, дописал в начало:
                input = [['foo', 'bar', 'tar'], [1, 2, 3]]

                Все! 10 минут, и на рабочем столе лежит вот такой документ:



                Я прекрасно понимаю, что вы взяли это просто как пример. Но положение дел сейчас таково, что для не самой распространенной задачи на php вы скорее всего найдете пару полурабочих библиотек, хорошо если написанных на ооп (про psr-0 и не мечтайте).
              • 0
                Речи нет про использование языка. Вы конечно можете перейти на Ruby. И делать те же задачи. Но (к примеру) вам нужно сделать выгрузку в xls и встает проблема. На php это решается за 20 минут (найти библиотеку + разобраться в ней). На руби к примеру может занять неделю. Или нужно сделать выгрузку из TecDoc. Да даже интеграцию с платежной системой нужно изучить сначала на php и потому переписать на руби.

                Полностью поддерживаю ваше заблуждение. Я когда после 8 лет работы на PHP, только сел на питон, притом на живом проекте, с реальной задачей. А не за пивом в выходной. Задача была схожа с вашей. Сграбить «название-цена» с 3х достаточно крупных инет-магазинов. А потом выгрузить их в xls файлы. На пхп я написал задачу за сутки где-то.
                Потом переносил на питон неделю.
                И так же как и вы плевался на Python и боготворил PHP.
                Но месяц не прошёл, когда я понял что такое питон, и осознал ущербность PHP. Только тогда я изменил своё мнение. Потом, спустя пару месяцев, я под новый инет-магазин писал условия граббинга уже за час-два. На Python`e.
                И да, я как и вы почти месяц, токо попробовав, но не поняв, ругал «другие языки» и хвалил «удобный и понятный пхп».

                З.Ы. У того же питона «библиотеки» ищутся, ставятся и используются в разы быстрее и проще, да и кода в разы меньше писать приходится. Минусы свои тоже есть.
              • +1
                Вот только в контексте топика, такой «php» это совсем не php, наймете вы человека, будете считать что он знает php и сможет работать в команде с другими php программистами, а тут облом, на самом деле он знает свой язык, который по синтаксису является php, но использует такую нестандартную логику что другие разработчики воспринимают ее далеко не сразу.
                Т.е. такой программист применим только для задач типа: сделал — получил результат — выбросил код. Ну или задач в которых код никогда не меняется и не поддерживается (а такие бывают?). Иначе либо вам искать еще таких же нестрадратных программистов, либо забыть о возможности расширить команду проекта которым занимался этот программисти, или передать этот проект другому без кардинальной переделки.
                • 0
                  Начнем с того что я не буду нанимать такого человека =) А даже если он запудрит мне мозги при собеседовании через месяц его уволят. Тем более что практически все проекты разрабатывают на фреймворках своих или чежих. И он сможет или писать код в стиле фреймворка или не писать его вообще.
                  А с точки зрения мы наняли человека и пиши как хочешь то на любом языке можно писать непонятный и ужасный код. Непонятно почему вы упомянули только PHP. Ну да. На нем много говнокодеров. Но их элементарно отбраковывают. А на проекты всегда ставится определенный стандарт написания кода. Это общая проблема всех языков.
                  • 0
                    Я сам пишу на PHP, никаких холиваров. Просто ветка началась с «А не проще язык поменять?», а Вы начали приводить примеры насыщенности рынка программистами и области готовыми решениями. Я лишь уточнил что ни то что у такого программиста есть знания php, ни готовые решения в данном случае ничего не дадут, потому что он фактически пишет на своем языке. И лучше уже ему поменять язык, потому что тогда он хотя-бы будет не один такой уникум.
                    Речь ведь шла именно о смене языка программистом (раз уж ему нравится функциональное), а не проектом.
                • +1
                  Скажу по себе: что в PHP есть возможности ФП я знал ещё во времена PHP3, и был готов с ними встретиться в чужом коде, хотя их использования избегал. Да даже сам использовал всякие array_* ради семантической частоты. И ни разу не составляло труда перевести его в термины for(each) или наоборот.

                  Имхо, человек который не может сопоставить «чистый» array_walk() c foreach (и наоборот) и обосновать свой выбор, звания PHP-программиста не достоин.
                  • +1
                    вы видите разницу между может разобраться и комфортно работать? я и в лапше, и в говнокоде способен разобраться, но это не значит что мне удобно будет развивать такой код. функциональное, это другой подход, способ мыслей, и перестраиваться на него только потому что кому-то скучно работать по логике языка, ну совершенно не хочется.
      • –1
        «Фейсбук написан на PHP» — лишь городская легенда.
        Там очень много всего намешано, и даже для PHP они писали собственный компилятор в нативный код.
        • +1
          Ничто не мешает использовать этот компилятор и остальным — код открыт.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +5
        Стоит. Разработчиков на Ruby, Python и Perl, к примеру, очень тяжело найти. И зарплаты у них в среднем выше.

        Это из личного опыта, как со стороны соискателя, так и со стороны работодателя.
        • +8
          Честно говоря для меня это выглядит как какой-то синтаксический ад:
          use React\Curry;
          array_map(Curry\bind('strpos', Curry\…(), 0, 1), ['foo', 'bar', 'baz']);
          
          • +2
            Ну хоть [] добавили.
            Ещё лет 5 и пых станет похож на что-то удобное.
        • +1
          Так же как и заказ на Ruby, Python и Perl сложнее найти, а от этого и цены для заказчиков выше, то что на PHP будет разработано за 500$ и за 20$ в год работать, на других технологиях обойдется дороже.
        • +2
          А где вы зарплаты смотрите? Хотелось бы посмотреть на зарплаты рубистов.
          Потому что я видел как раз обратное.
          • 0
            Я сейчас в компании, где используют Perl. Вакансии типичные можно глянуть на reg.ru или masterhost.ru.

            • 0
              У masterhost зарплаты не указаны, а на reg.ru они смехотворны.
              • 0
                Странно. Ну год назад ведущему в .masterhost предлагали 150, если не ошибаюсь. Точно сказать не могу.

                Касательно reg.ru, — у нас удаленка. Соответственно зарплата сильно зависит от места проживания.
                • 0
                  Ведущему. В PHP такие тоже бывают.
                  А средние 80-120.

                  Что касается удаленки, то зарплата такая же должна быть, как в Москве, иначе нет смысла работать за меньшие деньги, когда можно просто переехать.
                  • 0
                    Думаю, это спорный вопрос. В любом случае диапазон по зарплате — легко можно изменить, было бы за что.
                  • +1
                    В Москве дорогое жильё, поэтому переезжать смысла нет.
                    • +1
                      Ну вот если разница в зарплате больше 20тр (разница в стоимости аренды жилья), то смысл есть.
                      • 0
                        20тр это довольно хреновое жилье в Москве.
                        • +1
                          А 30-35 — не хреновое уже. Я указал, что 20тр — это разница в стоимости аренды.
                          • +1
                            Ну да за 35 уже нормальное жилье.
                            Но где тогда смысл? При разнице в 20тр вы получаете потерю в 15тр от зарплаты. Но получаете гораздо худшие условия для жизни. Особенно если дети есть. Москва не очень хороший город для жизни. Я бы стал рассматривать переезд в Москву при разнице в зарплате от 40 и выше.
                            Хотя все индивидуально. Кому то наоборот, кроме Москвы ничего не нужно.
                            • 0
                              При разнице в 20тр вы получаете потерю в 15тр от зарплаты
                              Вы забыли, что зарплата в Москве и провинции отличается. Более, чем на 15-20тр, которые являются разницей в стоимости жизни.
                              • 0
                                Тык вы сами написали при разнице в 20тр и больше. А я говорю, что про раницу от 40тр и больше.
                                И разница в стоимости жизни имхо больше 15-20тр. Ну и про разницу в качестве жизни я тоже написал.
                            • 0
                              Вы забыли, что в исходном городе тоже жильё не бесплатно. Снимали в маленьком городке за 15, в Москве такого качества — 35. Если предлагают зарплату на 20 больше в Москве, то потерь нет.
                              • 0
                                Ну в целом вы правы. Я просто по себе сужу. У меня в исходном городе жилье бесплатное почти :)
                • 0
                  И как же при смене языка с PHP на Perl/Ruby/Python сможете претендовать на зарплату ведущему?
                  • 0
                    Ну я с PHP ушел на Perl, сразу был ведущим. Навыки и опыт от языка слабо зависят. Также потом был ведущим, когда поменял Perl на Ruby.
                    • 0
                      Могу позавидовать. На всех вакансиях Python или Ruby уровня выше Junior я ни разу не видел «можно без опыта» — обычно минимум год-два разработки на конкретном языке и конкретном фреймворке требуется.
                • +1
                  Вот интересно, почему удаленная зарплата зависит от места проживания.
          • 0
            А про Ruby, — типичные вакансии — на undev.ru
  • +4
    Очень люблю функциональщину, влюблен в Scala. Поэтому все время останавливаю себя от функциональных вызовов в php. Медленно это (в плане вызова функции). Опять же разнобой в порядках параметров в функциях (array_map,array_reduce), а до 5.3 вообще костыльно выглядели вызовы.
    В 5.3 есть проблема в том, что в замыкание нельзя передать $this (появилось в 5.4), урезана работа с ассоциативными массивами (не добраться до ключей).
    Но, вцелом, хоть что-то.
    • 0
      И рекурсия в php медленная.
    • +2
      Что значит «не добраться до ключей»?
      • 0
        Перечитал текст своего комментария, понял в чем вопрос. Это безотносительно к 5.4. Речь шла о ключах ассоциативного массива, которые не передаются в array_map/array_reduce.
    • 0
      Colada не смотрели, случаем? Scala тут один из источников вдозновения.
  • –6
    Насчет функциональности — каким бы синтаксис не был, все полезно! Это круто! Мне этим JavaScript еще прет! А вот такой пипец всегда бесил.
    $print = 'printf';
    $print("Hello %s\n", 'World');
    
    • 0
      Как насчет? Не удобно ли скажете Вы?

      class IndexController {
          public function indexAction() {
      
          }
      
          public function saveAction() {
      
         }
      }
      
      $controller = new IndexController();
      $action       = $_GET['action'] . 'Action';
      $controller->$action();
      
      • 0
        да, про такой вариант я забыл. Сам так часто делаю
  • +7
    Многие функциональные языки имеют тщательно продуманную систему типов, а в php это даже системой назвать нельзя
  • +5
    Ну очень вымученный пример. Почему не записать так?

    function calculate_totals(array $cart, $vatPercentage)
    {
        $vat = $vatPercentage / (100 + $vatPercentage);
        $gross = array_sum(array_map(function($position) {
            return $position['price'] * $position['quantity'];
        }, $cart));
    
        return [
            'gross' => $gross,
            'tax' => $gross * $vat,
            'net' => $gross * (1 - $vat),
        ];
    }
    
  • –1
    Знаете в чем прикол?

    Напишите эту логику на стороне БД, и работать иногда будет раз в 10-100 быстрее. :)
    Иногда скорость важнее красоты
    • +1
      Важнее всего — возможность дальнейшей поддержки и развития продукта. Покажите мне систему контроля версий для БД и нормальное IDE.
      • +2
        Система контроля версий — это система контроля версий файлов. Хоть вордовские документы храните.

        Нет никаких проблем в ней хранить скрипты, которые разворачивают или альтерят базу. IDE тоже разные есть. Но вообще-то не так прямо там они и нужны. Пошагово запрос все равно не выполните.

        Важнее всего — возможность дальнейшей поддержки и развития продукта

        Это верно. Но как показывает практика, как раз БД — самое оно для этого. Правильная схема данных и побольше ее грузить тем, чем должна заниматься — обработкой данных. Возможность изменить вычисления, поменять запросы и т.д. — в базе делаются гораздо быстрее и безопаснее, чем где-то в коде императивного языка.
        Правда, надо и уметь писать запросы. Т.к. встречал такие проекты, где каждая хранимка утыкана ифами, циклами и тысячами строк.
        • +1
          Система контроля версий — это система контроля версий файлов. Хоть вордовские документы храните.
          И что я с ними делать буду? Как мне быстро откатить какую-то правку в хранимках? Вытаскивать файл, сочинять ALTER? Ерунда же полная.

          IDE нужны, конечно. Посмотреть что куда ссылается, навигация между хранимками, триггерами, отладка и прочее. Нет таких IDE. После какого-нибудь Visual Studio посмотришь на IDE для БД, кажется, что в прошлый век попал.

          99% времени жизни проекта нормальной сложности — его поддержка, скорость работы это хорошо, но 99% времени нужно быстро фиксить баги и дописывать к старому что-то новое. А тут у БД полный ад с этим.
          • 0
            Да есть и IDE и которые на себя откаты берут.
            Правда, я не такой уже и базовик, чтобы знать их. Для майкрософт просто мне достаточно Management Studio. Есть в VS тоже, получше. Правда в дорогой версии Ultimate. Но если пользоваться пиратской — без разницы. И та и другая поддерживают навигацию.

            Есть получше. Не пользовался, но базовики пользуются ERwin.
            99% времени жизни проекта нормальной сложности — его поддержка, скорость работы это хорошо, но 99% времени нужно быстро фиксить баги и дописывать к старому что-то новое. А тут у БД полный ад с этим.

            Это всё верно. Но как раз критика БД не к месту. Обычно, просто люди пишут коряво скрипты и запросы, а потом жалуются, что там ад. Как минимум, почти как факт надо воспринимать, что любой запрос, даже очень сложный, пишется за час-два. При правильной схеме это значит, что почти любое требование вы реализовываете за час-два. Это скорость разработки. Почему так получается? Потому что код более емкий информационно и меньше шансов ошибиться, попасть в бесконечный цикл почти невозможно, переполнить стек не возможно.
            А писать на каком-то императивном языке с развитым IDE с возможностью дебажить (в БД такие IDE тоже есть) — это все равно что писать неизменяемый, опасный код, в котором всё пишется с нуля, куча решается проблем, не связанных с задачей, а значит и много много багов — но зато, можно в этих багах с удовольствием копаться.

            Скрипты и БД — настолько просто всё, что даже при полном отсутствии IDE и средств для сохранения в системе контроля версий или при отсутствии средств для написания юнит-тестов — это все можно сделать/настроить за один /два дня.
            Ад с БД встречается и часто. Но это не БД виновато, а руки кривые. В большинстве случаев программы пишут программисты, у которых SQL не является хорошо проработан. Поэтому, получается в базе говнокод. И сталкиваясь с этим, программисты стараются как можно больше логики писать на своем языке, перетягивая одеяло у СУБД. Последние разрабатывают, как сервера, для обработки данных, оптимизируют, обычно включают мощные средства авторизации — полноценный сервер. А программисты все же поверх него говнокодистый сервер пишут всегда, разрушая строгие проверки целостности данных, пишут свой механизм транзакций, дублируют данные (кешируют). Короче, дописывают такой же по задачам только хиленький сервер.
            • +1
              Чудовищное поделие этот ERWin.

              Вы так говорите, как будто БД сразу с пользователем взаимодействует. Как только программисты уровнем выше придут в первый же раз выяснять отношения, сразу начнутся костыли и прочие прелести. И опять будет ад. Именно ещё и поэтому проще не разделять приложение на две части, а писать сразу логику на уровень выше.
              • –1
                Это зависит от программистов.

                Да, сейчас есть проблемы. Проблемы с кадрами в первую очередь. Знания людей влияют на разработку в целом. Если не хватает ума написать запрос, то обычно в коде программиста и на уровень выше будет редкий говнокод, размазанный ровным слоем по куче файлов и классов.

                Спорить начинать не хочу на эту тему, про частности, про СУБД. А просто про нечто в вакууме скажу. Логика, написанная на уровень выше — не что иное, как конкурирующий сервер с тем, что ниже. Люди пишут велосипедище, причем опасное, с мегакостылями. Например, в базе данных декларативно моделируют с помощью связей — как должна проверяться целостность данных. Таким образом формируют некоторую модель предметной области. Но программистам на уровень выше — не нравится такая строгость и делают свой сервер, который ослабляет проверки или вообще уничтожает. Доходит до того, что думают, что СУБД вообще должна только хранить данные. Чушь несусветная. Если бы было так, зачем деньги тратить на СУБД? Оплачивать работу разработчиков оракла, майкрософта, которая вам не нужна? Если нужно только хранить данные — есть файловая система.
                Вообще, принципиально, данные не должны дублироваться. Они должны быть в одном месте — один источник. Как бы есть принцип правильной разработки — не дублировать код, не дублировать данные. Когда люди пишут на уровень выше, то по сути, они дубрируют данные, создают механизмы синхронизации. Плохо всё это.

                Данные и их обработка должны быть как можно ближе. Просто помечтайте, что СУБД не состояла бы из таблиц, а состояла из объектов и язык был бы ваш любимый (PHP, Java, C#). Вы бы и в этом случае выносили работу еще в один сервер сверху? Скорее нет. Теперь двигаемся на шаг дальше в воображении: такой гипотетический сервер на любимом императивном языке имеет смысл, если бы он мегаускорял работу с данными. Т.е. вы пишете на своем языке, а он умеет оптимизировать обработку большого объема данных. Хорошо было бы, но это невозможно в императивных языках. Тогда, дальше пытаемся представить: нужен какой-то язык, который бы ограничил работу как-то декларативно, чтобы сервер мог брать на себя работу.

                Вот такой язык и есть SQL. Он ограничен, но благодаря этому, он позволяет легко изменять вычисления при любом требовании. Императивные (ООП) языки сделаны по принципу — пишем опасный код, который всё может, но чтобы смогли контролировать баги при росте объема кода — скрываем код — за функциями, за классами. Реляционные БД сделаны по другому принципу — создаем декларативно отношения, создаем ограниченный язык, который можно применять ко всем данным открыто, не боясь что-то сломать. Это функциональный подход по сути.

                SQL не идеален. Возможно, будут более лучшие языки и сервера в будущем. Но методологически, неправильно думать, что правильно писать логику где-то еще.
                А сейчас, пока будущее не наступило, приходится действовать по обстоятельствах. Смотря в чем коллектив более опытный.
                • 0
                  Вы так говорите, как будто у SQL альтернатив нет. Я вам обратную ситуацию скажу, выкидываете SQL и общаетесь с хранилищем без него из своего любимого языка программирования. Почему бы не так?
                  • +1
                    Альтернативы SQL конечно, есть. Но нет пока распространенных таких СУБД.

                    Я вам обратную ситуацию скажу, выкидываете SQL и общаетесь с хранилищем без него из своего любимого языка программирования. Почему бы не так?

                    Так я вам эту ситуацию и описал выше.

                    В чем проблемы такого подхода? В языках.
                    1. Если вы выбрасываете SQL и общаетесь с реляционной СУБД как с хранилищем, то вы по сути вообще СУБД не пользуетесь. Мне нравится аналогия — купили очень дорогой, последней модели, танк. Привязали тачку и возите на нем копья. SQL — это то, зачем по сути создан сервер — это язык для обработки данных. Для обработки, очевидно, они и создаются. Иначе с чего бы так люди парились по поводу оптимизаций серверов.
                    Если вы хотите использовать СУБД как хранилище, тогда вам файловой системы более чем достаточно. Страшно, что в случае аварии данные потеряются? Так они и будут теряться в случае использования реляционной СУБД и вашей отдельной прослойки (пока данные не сохранятся в СУБД)
                    Сделать какую-то фиксацию на случай сбоя электричества в случае вообще нет никаких проблем. Только для этого нужна реляционная СУБД? Тогда нет никакой логики в покупке дорогих и даже дешевых СУБД.
                    Да, можно создать такую систему, в которой логика на «любимом» языке, а за ним хранилище. Только, по сути, вы создаете с нуля СУБД. Это и есть велосипед. Что такое транзакции в такой СУБД? На плечи программиста ложится. Что такое целостность данных? Что будет, если к одному и тому же по смыслу экземпляру начнут обращаться с разных потоков разные клиенты? На плечи программистов. Дедлоки? На плечи программистов. И т.д. Вы полностью создаете СУБД. Доказывать не буду, что она никогда не сравнится по безопасности с теми дорогими СУБД. Там команды состоят из неглупых людей.
                    Чем еще плох подход, когда пишут на хранилище. Тем, что у вас задача «купить хлеб», а не построить хлебный магазин и купить в нем хлеб.
                    2. Если любимый язык не императивный, там уже возможны варианты.

                    Я не сверхсторонник реляционных баз данных и языка SQL. Альтернативы скорее всего будут, в скором времени появятся. Идея в воздухе давно висит. Не выстреливает пока из-за того, что большинство программистов сидят на игле ООП и считают, что именно ООП — правильно и такие возможно должны быть сервера. ООП с++-ного типа не очень клеится к надежности с одной стороны, а с другой не очень клеится с самой возможностью создать такой сервер, который брал на себя работу по обработке данных. А если такой сервер не будет брать на себя хотя бы часть работы, то это и не сервер вообще, а файловая система с компилятором
                    • +2
                      Если вы выбрасываете SQL и общаетесь с реляционной СУБД как с хранилищем, то вы по сути вообще СУБД не пользуетесь
                      Я про реляционные СУБД и не говорил, а потом пусть даже реляционные. SQL не единственный язык для этого.

                      Остальное я не понял. Какая файловая система, о чём вы (кстати, файловые системы тоже кластерные бывают, ничего на них у вас не потеряется, так же посмотрите что такое «дисковые полки»)? Какие велосипеды? «Альтернативы скорее всего будут»? Вы NOSQL никогда не видели что ли? Им лет-то больше, чем реляционным с SQL, сейчас новая волна популярности как раз, среди NOSQL есть и реляционные, для работы с графами, например, Neo4J, вполне себе «реляции», только сетевые.
                      • 0
                        Просто реляционные СУБД пока лидируют и я их как образец и представляю.

                        Работаете на PHP/C#/Java/C++ с любой СУБД как с хранилищем, я-ля «дай мне объект с таким айди», то вы будете в своем любимом языке реализовывать все прелести СУБД (реляционных, например, в которых уже это реализовано).
                      • –1
                        про NoSql я пока говорить не хочу. То в большинстве случаев просто огрызки функциональности реляционных СУБД. В угоду оптимизациям. Велосипедостроители побеждают — количество переходит в качество. Вот и волна популярности.

                        Про файловую систему, как мне казалось, всё понятно. Что есть транзакция? То, что в файловой системе сохранится и не потеряется, не спорю, это проблемы файловой системы. А вот согласованность данных в этой файловой системе проблемы СУБД. Если уже настолько тупая СУБД, что она только скидывает данные и поднимает, то по крайней мере надо реализовать транзакцию записи одного объекта. Потому как сбой может произойти «в процессе». И файл не будет иметь нужную структуру.

                        Так вот, сделать такую СУБД — работа порядка часов. Удивляет, когда для этого используют реляционную СУБД и со святой уверенностью утверждают, что так и должна выглядеть правильная архитектура.
                        • +1
                          Вы про NOSQL не хотите говорить, а я не хочу хранить данные в файловой системе. Зачем мне это, есть есть какой-нибудь Riak.

                          Ну и NOSQL бывают сильно сложнее, чем «вытащить объект по ID».
                          • 0
                            Вы про NOSQL не хотите говорить, а я не хочу хранить данные в файловой системе. Зачем мне это, есть есть какой-нибудь Riak.

                            Не равносильный обмен. Я ни в коем случае не говорил, что надо писать велосипед, вроде своего хранилища. Я просто намекнул, что хранилище можно написать в зависимости от простоты от полудня до двух дней. И приводил этот пример для того случая, когда люди «создают архитектуру» на серьезных реляционных СУБД, доводя работу с ними до абсурда.

                            Тут логика разговора была такая: реляционные СУБД <-> NoSql (как обрезанный функционал) <-> самодельное файловое хранилище. Которое, часто, мало чем от не самодельного будет отличаться.
                            Т.е. вы не хотите писать велосипед на файловой системе. Это хорошо. Зато собираетесь писать велосипед из транзакций, поддержки целостности данных.

                            Ну и NOSQL бывают сильно сложнее, чем «вытащить объект по ID»

                            Конечно. Только они, повторюсь, в большей части — просто огрызки из функционала. Да, иногда это надо. Иногда под некоторые задачи.

                            Будете смеяться, но именно в это время я пишу свой собственный нереляционный сервер для обработки данных. Но опыт написания уже не первого мне как раз говорит: не фиг их в абсолютном большинстве писать. Там в реляционных много есть вещей, далеко не простых, уже реализованных.

                            Нереляционные СУБД — это видимо от нефиг делать, от недостатка романтики в жизни программистов. Программисты вдруг вспоминают, что они могут еще и алгоритмы писать. И давай где надо и где не надо писать велосипеды. Даже мода появилась. Чуть ли не каждый второй разработчик «бизнес-приложений» мнит себя разработчиком высоконагруженной системы. А мега объем для его воображения — 100 пользователей и 100 000 записей в таблице.

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

                            Нереляционные решения — это либо для датамайнинга, либо для несложных, но высоконагруженных систем. Ключевое слово — несложных, вроде какой-то социальной сети.
                            Когда появляется мода, инструменты узкого назначения начинают тыкать куда надо и куда не надо. Реляционные СУБД достаточно универсальны и нужны всегда серьезные основания, чтобы выбрать другие решения.

                            И конечно, нереляционные решения бывают сложнее чем файловое хранилище. Но мы то как раз говорили о модели — на любимом языке писать логику, а сервер — хранилище.
                            • 0
                              Какой-то у вас подозрительный взгляд на NOSQL. С чего бы талантливым ребятам из Яндекса писать своё хранилище Elliptics вот уже несколько лет тогда? Я немного знаком был с проблемами, которые там встречались, двумя днями там и не пахло.

                              Ну и взгляд на нереляционные БД странный до невозможности. Это не от нефиг делать, у этого инструмента есть своя чёткая ниша. В частности, в какой-нибудь «Монге» я могу хранить документы с произвольным набором полей, это крайне удобно и такая потребность часто встречается в программах докуменооборота, например.

                              Или вот, как хранить и обрабатывать граф в реляционных БД? Помрёшь же сколько надо всего написать.

                              Ну и, например ещё, где хранить кеш как в нереляционных БД, к примеру?

                              Таких ниш — несколько больше, чем я перечислил.
                              • 0
                                Нет, не подозрительный. Мы просто пережевываем тему — а почему бы БД не быть просто хранилищем, а нам не писать свою логику. Вот, хранилище делается за пару дней.

                                Даже и графы. Нет ничего сложного там, где надо потом допиливать руками.
                                Вот про талантливых ребят и Яндекса — пойдет. Но, все прямо у нас разработчики Яндекса. Яндекс, как и Гугл — у них есть узкая ниша — вебмайнинг. Как раз написать базу такую, как основу, можно за пару дней. А потом несколько лет трахаться с алгоритмами, пытаясь найти лучшие, чтобы соорудить класстеризации, классификации, релевантный поиск. Там не с базой проблемы.

                                Граф хранить и обрабатывать в реляционных БД, можно. Хотя сложно и они неоптимально это будут делать. Тут от задачи зависит. Подозрительно, когда по умолчанию берут и выбирают базу на графах, просто так, потому что так удобно.

                                Как минимум, в приложениях для бизнеса с ходу назову пару проблем, с которыми точно столкнетесь. На какой нибудь Монге храните документы, которая удобна для этого. А деньги, обработку других данных, на реляционной. Точно, на Монге это будет менее удобно. Завтра приходит заказчик и говорит:
                                — А мне захотелось, чтобы мы получили графики, как зависит наша прибыль от документооборота.
                                И всё. Теперь возникла почти нерешаемая проблема. Вопрос: заказчик дурак, не понимает, что лучше хранить документооборот в Монго, даже если потом данные нельзя вместе обрабатывать?
                                Данные, разделенные на разные базы — тоже очень нехорошо. Сейчас важна универсальность и скорость разработки. Границ между данными быть не должно, иначе сильно плохо влияет на возможность быстрых изменений. На Монго написать всё тогда. А всё — это значит половина функционала реляционной СУБД руками пишется. А это также значит, если всё в Монго, что в случае нового пришествия заказчика, будете не пару часов на запрос просить, а полгода на переделку. Потому что не так планировали связи между данными и т.д.

                                В общем есть ниша у NoSql, единственное, его пихают везде.
      • 0
        Миграции как система контроля версий БД и были придуманы.
        Современные IDE умеют работать с SQL, какие Вам нужны еще фичи?)

        Полностью согласен что переносить логику в БД ужасная идея:-)
        • 0
          Ну вы что, правда не видите различия между миграциями и git'ом? Какие IDE умеют анализировать хранимки, показывать что из чего вызывается и переходить на код хранимки по клику на имя? Ну и так далее? Т.е. чтобы с SQL как с обычным кодом?
    • 0
      А как на счет масштабируемости? Базу масштабировать сложнее, чем аппликейшен сервера обращающиеся к ней, соответственно базу лучше грузить по минимуму.
  • –6
    PHP всегда был простым, процедурным языком программирования, черпавшим свое вдохновение из C и Perl.
    .
    правда, он всё не так понял, и в итоге стал таким какой есть. Ни Си, ни Perl и рядом с этим г. не стояли. так что, слишком много чести сравнивать его с этими великими языками.
    пхп-киддисы, минусуйте, я жду.
    • +1
      Конечно глупо отвечать на такие комментарии, но Вы видимо немного отстали от времени…
  • 0
    «Шуруп, забитый молотком, держится лучше, чем гвоздь, закрученный отверткой» (ц)

    Ради чего надо «функционально программировать на PHP», если код от этого становится и запутаннее, и медленнее?

    А «трюки» типа
    $controller = new IndexController();
    $action       = $_GET['action'] . 'Action';
    $controller->$action();
    
    — готовая уязвимость для хакеров, которые засунут в $_GET['action'] какой-нибудь private_method%00
    • +1
      Можно подробнее, как снаружи объекта можно вызвать приватные методы?
    • 0
      Вот только не надо, а? Это 13-ти секундный пример на коленке с проверкой на ничего.
  • +3
    Нет, ну если just for fun, то пожалуйста, но надеюсь никому не придет в голову применять это в реальном долгосрочном и/или командном проекте. Да вот за такое:
    use React\Curry;
    array_map(Curry\bind('strpos', Curry\…(), 0, 1), ['foo', 'bar', 'baz']);
    другие разработчики, не функциональщики, проклянут. Фактически на другом языке написано. Язык ведь это не только синтаксис, это его логика, подходы. Это позволяет нескольким разработчикам знающим 1 язык работать вместе. Да человеку незнакомому с С# например, но хорохо знающему PHP, проще будет прочитать код на С# чем разобратся вот в таком «php». Хочещь быть функциональщиком, используй языки изначально под это заточенные. А тут получается что разработчик либо всегда работает один и над проектами которые не могут достатся другому программисту, либо ему нужно в команду таких же функциональщиков на php. И тот и другой случай практически нереален. Ну это если по правильному, есть конечно же еще вариант когда человек пишет как ему нравится, а на остальное наплевать.
    • 0
      Частично согласен с вами. Но лишь частично. Имхо, у другого программиста не должно быть повода заглядывать в тело метода и, соответственно, ему должно быть всё равно реализован он с помощью ФП или ИП.
      • +1
        ну это в идеальной вселенной, где существуют идеальная архитектура, продуманные на годы вперед тз и прочие не встечающиеся в реальности вещи.
  • –1
    Где бенчмарки? Иначе этодемагогия. Первый код скорее всего самый быстрый.

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