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



В следующий раз о входных аргументах я задумался, когда впервые столкнулся с необходимостью разбора параметров командной строки в С/C++. Ну, помните это классическое:


int main(int argc, char* argv[])

Всего два входных параметра функции, но в них уложены все возможные комбинации CLI-параметров программы. Да, за универсальность вызова пришлось заплатить сложностью парсинга, но вариантов решить задачу кардинально по-другому как бы и нет.


Следующий раз задуматься о входных/выходных аргументах пришлось при анализе promises:


get('story').then(fnSuccess1).then(fnSuccess2). ... .catch(fnError);

В цепочке промисов выходные параметры одной функции передаются на вход другой (первым аргументом), видоизменяются и передаются далее, далее, далее… Получалось так, что для подобного конвейера важна была именно универсализация: один выходной аргумент — один входной аргумент.


Дальше — больше. В JavaScript нет перегрузки функций, поэтому там никого не парит такой код:


function sayHi() {
  for (var i = 0; i < arguments.length; i++) {
    alert( "Привет, " + arguments[i] );
  }
}

sayHi("Винни", "Пятачок"); // 'Привет, Винни', 'Привет, Пятачок'

После долгого общения с Java mistress такой код выглядит, словно обкуренный хиппи-шестидесятник рядом с офицером спецназа на боевом задании — "У этой функции вообще нет аргументов? Ну и что, расслабься, чувак! Давай-ка я скручу тебе arguments… На вот, дерни, и сколько бы тебе там чего не передали — все увидишь".


Более того, в JavaScript'е я есть такая функция, как apply:


fun.apply(thisArg[, argsArray])

назначение которой — применить функцию в заданном контексте с заданными аргументами. Здесь тоже никого не волнует, сколько входных аргументов ожидает увидеть сама функция (или ее автор) — задавай сколько хочешь.


В моей PHP-практике был случай, когда в стороннем модуле при перегрузке базового метода поменяли местами два аргумента (восьмой и деятый, или даже девятый и десятый — не суть важно).


public function doGood($a, $b, $c, $d, $e, $f, $g, %h, $i, $j) {}

public function doGood($a, $b, $c, $d, $e, $f, $g, %h, $j, $i) {
    parent::doGood($a, $b, $c, $d, $e, $f, $g, %h, $i, $j);
}

Для простых случаев их значения были равны, поэтому "от перемены мест слагаемых сумма не изменилась", у меня была конфигурация посложнее, поэтому бага и вылезла (вернее, я ее долго отладчиком выщемлял). Я после того отладочного дайва как-то более тепло, что ли, смотрю на конструкции типа:


func(opts)

или


Response resp = service.call(req);

Кстати, последняя конструкция у меня прочно ассоциируется с BDSM BPMS — был как-то интересный опыт привязки intalio к web-приложению. Каждый процесс состоял из отдельных операций, которые имплементировались web-сервисами по схеме "y = f(x)", где x и y сводились к XML-документам (иногда простым, иногда не очень), а "программирование процессов мышкой" к маппингу элементов игреков одних сервисов на элементы иксов других сервисов.


Возможно поэтому в своей текущей практике (PHP) я попытался свести некоторые группы функций к схеме "y = f(x)". В JavaScript это делается легко и приятно.


Было


function showWarning(width, height, title, contents) {
    var width = width || 200; // если не указана width, то width = 200
    var height = height || 100; // если нет height, то height = 100
    var title = title || "Предупреждение";
    var contents = contents || "Ничего серьезного.";
    //...
}

Стало:


function showWarning(opts) {
    var opts = opts || {}; // если отсутствуют входные параметры
    var width = opts.width || 200; // если не указана width, то width = 200
    var height = opts.height || 100; // если нет height, то height = 100
    var title = opts.title || "Предупреждение";
    var contents = opts.contents|| "Ничего серьезного.";
    //...
}

В PHP приходится платить разработкой структуры входных/выходных аргументов в отдельных классах. В основном получается муторно, но иногда — весело.


К чему я это все? А к тому, что сейчас я уже не думаю, что было бы хорошо возвращать множество параметров вместо одного "y", а задумываюсь над тем, в каких случаях было бы хорошо иметь на входе всего один "x".

Поделиться с друзьями
-->

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


  1. lair
    16.02.2017 12:19
    +5

    Добро пожаловать в функциональное программирование.


    1. flancer
      16.02.2017 12:21

      Поражаюсь вашей скорости чтения, коллега.


      1. customtema
        16.02.2017 14:30

        У меня тоже заняло секунд 20. Наверное потому, что я давно и много занимаюсь и программированием, и чтением блогов. В чем проблема?


        1. flancer
          16.02.2017 14:48
          -1

          У вас тоже? Вы с коллегой lair'ом там что, на скорость чтения соревнуетесь? В таком случае примите от меня респекты и в свой адрес, коллега.


  1. Andy_U
    16.02.2017 12:48
    +5

    Переходите на Python… Там можно «из коробки» вернуть из функции несколько значений и, или присвоить их одной tuple, или сразу «рассортировать» по отдельным переменным.


    1. mayorovp
      16.02.2017 13:09

      Или на последние версии javascript (ECMA 2015)


    1. MacIn
      16.02.2017 17:48

      В GO тоже можно несколько значений вернуть, если не путаю.


      1. MacIn
        16.02.2017 17:55

        Ну, да: hour, min, sec := time.Now().Clock()


  1. basili4
    16.02.2017 13:19
    -1

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

    https://ideone.com/Y8FkAO


  1. roman_kashitsyn
    16.02.2017 13:20
    +5

    Многие вещи становятся проще, если предполагать, что у всех функций всегда один аргумент.


    Математик и логик Хаскелл Карри ещё в первой половине 20 века ввёл концепцию "каррирования" — превращения функций многих переменных в функцию одной переменной. Эта техника широко используется в языках, поддерживающих функциональную парадигму, например, Haskell и OCaml.


    В Standard ML пошли немного другим путём: функции с "несколькими аргументами" на самом деле всегда принимают на вход один аргумент — кортеж.


    1. mayorovp
      16.02.2017 13:57
      +2

      Каррирование к тому, что обсуждается в этом посте, не имеет никакого отношения. Оно никак не упростит то же "программирование процессов мышкой".


  1. Eldhenn
    16.02.2017 14:00
    -3

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


  1. MacIn
    16.02.2017 17:59
    -1

    Из крайности в крайность: четыре внятных аргумента вида width height и так далее, которые влазят в экран и имеют говорящие имена, лучше, чем ватное opts, на мой взгляд; с другой стороны — десять аргументов — это уже нездорОво.


    1. flancer
      16.02.2017 18:03
      -1

      Виной всему — моя лень. Мне было лениво расписывать "нездоровый" вариант на десять аргументов, и я остановился на более коротком, надеясь, что идея будет понятной. В результате запорол пример. Ну что ж, буду работать над собой.


  1. devpony
    16.02.2017 20:09
    +3

    Ваша проблема на самом деле связана с типами. Меняя порядок следования аргументов в динамическом языке вы неявно меняете тип функции. В хорошем статическом языке такая ситуация невозможна. Предвосхищая замечания, для типов, одинаковых на машинном уровне, но разных семантически, существует newtype.


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


    1. am-amotion-city
      16.02.2017 20:40
      -1

      В хорошем статическом языке такая ситуация невозможна.

      Ну, давайте, расскажите, как ваша Панацея Статической Типизации поможет в случае перепутанных width и height в функции, принимающей всего-навсего два параметра.


      Хайп он такой хайп.


      1. devpony
        16.02.2017 20:53
        +4

        newtype Title = Title String
        newtype Width = Width Integer
        newtype Height = Height Integer
        
        showWarning :: Width -> Height -> Title -> String -> a
        showWarning (Width width) (Height height) (Title title) contents = undefined

        main = showWarning 1 2 (Title "Title") "Content"
        
        -- No instance for (Num Width) arising from the literal ‘1’

        main = showWarning (Height 1) (Width 2) (Title "Title") "Content"
        
        -- Couldn't match expected type ‘Width’ with actual type ‘Height’

        main = showWarning (Width 1) (Height 2) (Title "Title") "Content"
        
        -- Ok


        1. am-amotion-city
          16.02.2017 21:04
          -5

          OMG.


          Все, все, ухожу. Представляю, какая космическая скорость разработки у вас, если на каждый чих нужно пару типов объявить.


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


          1. devpony
            16.02.2017 21:31
            +4

            Все, все, ухожу. Представляю, какая космическая скорость разработки у вас, если на каждый чих нужно пару типов объявить.

            Мне — не нужно. У автора поста возникла проблема. Я утверждаю, что фундаментально это проблема не синтаксиса, а типизации и показываю как бы автор мог её решить на статически типизированном языке.


            И в базе так же лежат, дайте угадаю, сериализованные вместе с типами, да?

            Ну можно и с типами, если очень хочется. Правда не совсем понимаю, какое это отношение имеет к обсуждаемой теме.


            1. am-amotion-city
              16.02.2017 22:01
              -3

              Наипрямейшее. Автор говорит: «Смотрите, как можно упростить себе жизнь в джаваскрипте». Приходите вы, и говорите: «Проблема в языке, нужна статическая типизация».


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


              Например, потому что у нас фронтенд весь (такие мы осталые) на джаваскрипте. И переписывать его на Elm мы не станем. Или еще почему.


              И «ваш язык дерьмо» — крайне непрофесиональный подход. Задачи нужно решать в реальном мире, а не в хипстерском вакууме.


          1. HKA
            20.02.2017 16:51
            +1

            По теме «космической скорости»: использовали бы статическую типизацию для дюймов и метров, глядишь, Mars Climate Orbiter по-прежнему бороздил бы просторы Большого театра.


            1. mayorovp
              20.02.2017 17:08

              Не помогло бы. По радиосигналу информация о типах, увы, не передается...


        1. mayorovp
          16.02.2017 22:11
          -2

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


          1. devpony
            17.02.2017 02:30
            +2

            data Image = Image Width Height [[Int]]
            
            rotate :: Image -> Image
            rotate (Image (Width w) (Height h) xs) = Image (Width h) (Height w) $ transpose xs


            1. am-amotion-city
              17.02.2017 08:50
              -1

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


              h, w = w, h


              1. am-amotion-city
                17.02.2017 13:03
                -5

                Для минусующих религиозных фанатиков и просто дебилов поясню: хорошему программисту типы в 99% только мешают, а плохого программиста — никакие типы не спасут.


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


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


                1. flancer
                  17.02.2017 13:52
                  +1

                  Весьма категоричное заявление, коллега. Причем в нем не охвачены средние программисты, к коим я и себя отношу и коих по моему мнению — большинство. Мне, например, типизация (если она есть) весьма помогает ориентироваться не только в чужом коде, но и в собственном тоже (разумеется, при поддержке IDE). Не спасает, но и не мешает.


                  1. am-amotion-city
                    17.02.2017 14:05
                    -5

                    А я вообще весьма категоричный хрен.


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


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


                    1. flancer
                      17.02.2017 14:09

                      Чтобы оно, это ваше IDE, разобралось в типах, — языку типизация вообще не нужна

                      Объяснитесь, пожалуйста. Если досуг, разумеется.


                      1. am-amotion-city
                        17.02.2017 16:18
                        -3

                        Я же уже все написал: посмотрите, как dialyzer в эрланге сделан: аннотация типов. Вы просто описываете типы в критичных местах (или везде, если время девать некуда), как хотите, например: «вот эта функция принимает функцию, туплу, или строку, начинающуюся с "http://"», и пускаете в вашем CI среди прочего и dialyzer.


                        Так Elixir собирается, например, а у него в Stream там вообще весело, половина имлементаций (самые старые) — возвращают каррированную функцию с arity 2, а вторая половина — умные структуры, которые умеют reduce/2. И ничего, IDE живее всех живых все понимает, из-за аккуратно аннотированных типов.


                        А жесткая типизация приводит к тому, что изменилось стороннее API, данными которого мы пользуемся — и добро пожаловать в ад рефакторинга.


                        1. mayorovp
                          17.02.2017 16:29
                          +4

                          Зато без "жесткой типизации" изменилось стороннее API, данными которого мы пользуемся — и вместо падения сразу вся база оказывается забита строками "undefined" на месте обязательных колонок :)


                          1. am-amotion-city
                            17.02.2017 17:32
                            -4

                            Слушайте, ну вот зачем вы глупости-то говорите, а? Я где-то предлагал отменить валидацию данных? Между валидацией и жесткой типизацией — пропасть же. А вот эти с хайпом в голове — придут, почитают, и решат, что хороший код писать не нужно, достаточно очень жестких типов.


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


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


                            При этом мой неакадемичный подход заодно и строчку "undefined" вместо "http://example.com" развернет, потому что я сигнатуру функции буду матчить по префиксу, а прекрасные типы схавают и не поперхнутся. И вся база заполнится — сюрприз — андефайндами.


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


                            1. mayorovp
                              17.02.2017 19:14
                              +3

                              Вот-вот. Вам нужны аннотации, тесты, CI, валидация… А мне достаточно прописать пару типов!


                              При этом мой неакадемичный подход заодно и строчку "undefined" вместо "http://example.com" развернет, потому что я сигнатуру функции буду матчить по префиксу, а прекрасные типы схавают и не поперхнутся.

                              Существует такой тип как System.Uri.


                          1. basili4
                            17.02.2017 18:17
                            +1

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


                        1. flancer
                          17.02.2017 18:19
                          +1

                          аннотация типов

                          Получается, что самому языку типизация не нужна, но чтобы IDE могло в типах разбираться все-таки в каком-то виде (аннотации) эти типы указывать нужно.


                          А я вообще весьма категоричный хрен.

                          Вы полностью подверждаете свою собственную характеристику. Причем во всех частях. Похвальная самокритичность.


                          1. am-amotion-city
                            17.02.2017 20:15
                            -3

                            Пока получается только то, что вы либо читать не умеете, либо понимать прочитанное, либо и то, и другое вместе.


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


                            Потому что если я хочу, чтобы процесс упал и возродился — я просто напишу хендлеры на все понятные случаи входных данных и все. Получили бяку? — не волнует, хендлер не заматчится и процесс рухнет. Штатно рухнет, и супервизор его перезапустит. Вместо пяти экранов защитного кода.


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


                            1. michael_vostrikov
                              18.02.2017 10:19
                              +2

                              Вы правда не понимаете или прикидываетесь?

                              языку типизация вообще не нужна

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

                              — Указание типа это один из вариантов паттерн-матчинга
                              В Эрланге например можно написать так:
                              f(X) when is_integer(X) -> ...
                              В других языках указывается тип аргумента:
                              f(int x) { ... }

                              хендлер не заматчится и процесс рухнет

                              — В Эрланге есть процессы, которые встроены на уровень языка.
                              В отличие от многих других языков, где потоки создаются средствами ОС.

                              Вместо пяти экранов защитного кода
                              Надо написать пять экранов паттернов.

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


                    1. DarkEld3r
                      17.02.2017 17:13
                      +4

                      обсуждать нужды средних программистов мне тоже как бы недосуг: это как обсуждать стихи средних поэтов, интересно только автору.

                      Безотносительного всего остального, но эта аналогия совсем не к месту. Стихи — творчество и тут естественно выбирать лучшее, а вот в программировании работы хватает, так что задействованы не только "средние", а и "слабые" программисты. И инструменты позволяющие чего-то добиться имея слабую/среднюю команду однозначно интересны. Опять же, безотносительно типизации.


                      1. flancer
                        17.02.2017 18:10
                        +1

                        Полностью согласен, программирование — это craft, а не art. Воспроизводимость, дополняемость, инженерность, если хотите, здесь важнее уникальности или самобытности. Понимание этого приходит со временем и не ко всем.


                      1. am-amotion-city
                        17.02.2017 18:38
                        -2

                        в программировании работы хватает, так что задействованы
                        не только «средние», а и «слабые» программисты

                        И что? Ну, обкрадывают эти люди доверчивых гуманитариев, которые убеждены, что программирование — это рокетсайенс (а на самом деле — ремесло, не сложнее плотницкого, а во многом — гораздо проще).


                        Имея слабую/среднюю команду, имеет смысл эту команду обучать, а не топор точить.


                        1. DarkEld3r
                          17.02.2017 18:55
                          +2

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


                        1. flancer
                          17.02.2017 19:17
                          +2

                          имеет смысл эту команду обучать, а не топор точить

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


              1. Sirikid
                18.02.2017 23:44
                +2

                h, w = w, h

                А как же пиксели? Нельзя просто так взять и поменять высоту и ширину местами.


                mayorovp


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

                import Data.Vect
                
                data Width = W Nat
                data Height = H Nat
                
                data Image : (w : Width) -> (h : Height) -> (colors : Nat) -> Type where
                  I : Vect w (Vect h (Fin colors)) -> Image (W w) (H h) colors
                
                rotate : Image (W w) (H h) c -> Image (W h) (H w) c
                rotate (I pixels) = I (transpose pixels)

                А как вам такое? Это правда не Haskell, а Idris, но от этого становится только веселее, потому что можно зашить в тип изображения его ширину и высоту и явным образом выразить суть функции rotate в её типе. Кстати, сама функция сгенерирована почти полностью на основании этого типа, руками я написал только вызов transpose.


                1. am-amotion-city
                  19.02.2017 10:16
                  -1

                  Это академический кошмар, который никогда не попадет в продакшн. Поймите, в теории это все выглядит очень красиво, даже я это признаю?.


                  Но на практике мы непременно захотим, скажем, масштабировать по длинной стороне до 600px (если ландшафт — 600 ширины, если портрет — высоты.) Или еще что (не нужно выкладывать очередную простыню изящного на ваш взгляд кода, я верю, что все возможно).


                  И смысл в том, что такие задачи появляются как бы три раза в день. Ширина и высота — это одна и та же сущность, описываемая в реальном мире одним и тем же: метрикой, взаимно однозначно отображающейся на множество целых положительных чисел.


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


                  1. Sirikid
                    19.02.2017 20:33
                    +1

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

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


                    Ширина и высота — это одна и та же сущность, описываемая в реальном мире одним и тем же: метрикой, взаимно однозначно отображающейся на множество целых положительных чисел.

                    Если ширина и высота выражаются одним и тем же типом это не значит что семантика у них одинакова, когда я создаю псевдонимы Width и Height я отражаю эту семантику.


                    Но на практике мы непременно захотим, скажем, масштабировать по длинной стороне до 600px (если ландшафт — 600 ширины, если портрет — высоты).

                    И эту семантику можно отразить в типе.


                    Может быть, неофитам помогает, не знаю.

                    Насколько я знаю неофитам как раз проще с динамической типизацией (но строгой, а не как в JS, лол).


                    это все избыточная ерунда

                    А ещё можно сгенерировать более эффективный машинный или байт-код на основе "этой ерунды".


                    Я понял вашу позицию. Жду ответа по поводу семантики, вывода типов и эффективности.


                    1. am-amotion-city
                      19.02.2017 20:55
                      -1

                      Жду ответа по поводу семантики, вывода типов и эффективности.

                      Императивы редко помогают конструктивной дискуссии.


                      это не значит что семантика у них одинакова

                      Строкой выше я написал, что это одна и та же сущность, что в переводе означает: семантика одна и та же. Удивлен, что вы не предлагаете заводить типы WidthOfSelfiePictureMadeOnMyLastBirthday: семантика-то тут явно глубже, чем у просто Width.


                      ещё можно сгенерировать более эффективный машинный или байт-код на основе «этой ерунды».

                      Это вашей барышне рассказывайте, она поверит. В теории можно. А на практике покажите мне распределенные high-load вычисления с вводом-выводом на Хаскеле. Premature optimization — гигантское зло. Сначала надо уметь взлетать, а потом уже оптимизировать.


                      1. Sirikid
                        19.02.2017 21:26
                        +2

                        Удивлен, что вы не предлагаете заводить типы WidthOfSelfiePictureMadeOnMyLastBirthday: семантика-то тут явно глубже, чем у просто Width.

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


                        А на практике покажите мне распределенные high-load вычисления с вводом-выводом на Хаскеле.

                        Cloud Haskell?


                        Императивы редко помогают конструктивной дискуссии.

                        Это был не императив, честное слово. По крайней мере я хотел вложить другой смысл, что мне интересно услышать ваш ответ.


  1. patricksafarov
    16.02.2017 22:08
    +1

    в последних стандартах js последний пример еще красивее пишется

    function showWarning({
        width = 200,
        height = 100,
        title = "Предупреждение",
        contents = "Ничего серьезного."
    }) {
        //...
    }
    


  1. AlexTest
    17.02.2017 03:33

    function showWarning(opts) {
        var opts = opts || {}; // если отсутствуют входные параметры
        var width = opts.width || 200; // если не указана width, то width = 200
        var height = opts.height || 100; // если нет height, то height = 100
        var title = opts.title || "Предупреждение";
        var contents = opts.contents|| "Ничего серьезного.";
        //...
    }


    В PHP приходится платить разработкой структуры входных/выходных аргументов в отдельных классах. В основном получается муторно, но иногда — весело.
    Судя по примеру на JS — вас не волнует, что там реально предается в opts, тогда вместо ваших «отдельных классов» просто используйте в PHP для тех же целей stdClass что-то типа такого:

    $params = new \stdClass;
    $params->width  = $x;
    $params->height = $y;
    
    function showWarning($params = null) {
        $params   = $params ?? new \stdClass; // если отсутствуют входные параметры
        $width    = $params->width ?? 200; // если не указана width, то width = 200
        $height   = $params->height ?? 100; // если нет height, то height = 100
        $title    = $params->title ?? "Предупреждение";
        $contents = $params->contents ?? "Ничего серьезного.";
        //...
    }
    



    1. AlexTest
      17.02.2017 04:37
      +1

      в первой строке функции, чтобы уж совсем красиво было, вместо Null-коалесцентного оператора лучше сделать приведение типа входного $params к объекту, а то вдруг там массив или еще что вместо stdClass:

      function showWarning($params = null) {
          $params   = (object) $params; // если отсутствуют входные параметры
          $width    = $params->width ?? 200; // если не указана width, то width = 200
          $height   = $params->height ?? 100; // если нет height, то height = 100
          $title    = $params->title ?? "Предупреждение";
          $contents = $params->contents ?? "Ничего серьезного.";
          //...
      }


    1. AlexTest
      17.02.2017 05:10

      Но вообще, такой подход к вводу параметров/выводу результатов — это даже не BDSM, это гарантированный отстрел всех ног да и рук тоже :)


    1. flancer
      17.02.2017 07:56

      Судя по примеру на JS — вас не волнует, что там реально предается в opts

      Не совсем так. Я бы с радостью задал ожидаемую структуру входного объекта и для JS, вот только пока не знаю, как это сделать. Лучшее, что я пока нашел:


      /**
       * ..
       *
       * @param {Object} opts
       * @param {string} opts.currency
       * @param {Object} opts.screen
       * @param {boolean} opts.screen.save
       * @param {string} opts.screen.prefix
       * @param {Object} opts.suite
       * @param {string} opts.suite.pack
       * @param {string} opts.suite.scenario
       */
      function doGood(opts) {
          var opts = opts || {}
          var suite = opts.suite || {pack: "undef", scenario: "undef"}
          var optsScreen = opts.screen || {} // screenshots related opts
          var saveScreens = optsScreen.save || false // don't save screenshots by
          var savePrefix = optsScreen.prefix || "default" // default prefix for screenshots
          var currency = opts.currency || "EUR"
      ]1

      Получается "масляное масло" — задавать структуру входного аргумента в док-блоке и парсить его же в теле функции в первых строках. Структура входных аргументов на PHP удобна всплывающими подсказками и autocomplet'ом (поддержка IDE).


    1. flancer
      17.02.2017 07:59

      Если бы можно было ставить ссылку на структуру в док-блоке, хватило бы примерно такого:


      /**
       * ..
       *
       * @param {Object} opts
       * @param {string} opts.currency
       * @param {Object} opts.screen - see @Screen.obj
       * @param {Object} opts.suite - see @Suite.obj
       */
      function doGood(opts) {}


      1. mayorovp
        17.02.2017 08:38

        А чем вариант @param {ScreenOptions} opts.screen плох?


        1. flancer
          17.02.2017 09:40

          У меня очень положительные впечатления от использования java packages & PHP namespaces, а вот в JS я подобного механизма не вижу. Когда PHPStorm пытается предложить мне автодополнение к началу какой-нибудь JS-функции, то он сперва долго "шуршит мозгами", а потом выдает сразу список всего, что нашел, причем одноименных функций из разных источников там столько, что автодополнением для JS'а я, например, пользоваться не могу. Возможно дело в том, что я работаю с Magento, а там куча не только своего кода, но и и кода из сторонних модулей, со своими JS-зависимостями. Иногда приходится из 5 JQuery-библиотек, которые тянутся модулями, оставлять одну посвежее, а остальные глушить (правда это для М1 было, с двойкой пока такого опыта не было).


          Надо будет попробовать ваш вариант на небольшом, отдельном проекте и понаблюдать за. Боюсь, правда, разбор всего содержимого каталога vendor вызовет примерно такой же эффект — куча одноименных функций без возможности идентификации источника кроме как по имени файла (даже без пути). Но если давать "кучерявые" имена для своих структур (VendorProjectModuleClass — чтобы у IDE даже сомнений не было, что я имею в виду), то такой вариант, я думаю, вполне даже хорош.