Когда-то, давным-давно, когда я еще учился программировать на Паскале, мне пришлось на лабораторке переделывать одну собственную функцию — нужно было в результатах возвращать не просто число, а число и строку. Как было бы хорошо, подумал я тогда, если бы в результате можно было возвращать множество переменных, как это сделано для входных параметров. Подумал, вздохнул и начал создавать структуру для передачи обратно в вызывающую функцию двух параметров за раз.
В следующий раз о входных аргументах я задумался, когда впервые столкнулся с необходимостью разбора параметров командной строки в С/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)
Andy_U
16.02.2017 12:48+5Переходите на Python… Там можно «из коробки» вернуть из функции несколько значений и, или присвоить их одной tuple, или сразу «рассортировать» по отдельным переменным.
basili4
16.02.2017 13:19-1ну в таком случае пхп для Вас идеальный язык. В нем можно в любую сторону любое количество параметров передавать. главное не забыть порядок как они передавались
https://ideone.com/Y8FkAO
roman_kashitsyn
16.02.2017 13:20+5Многие вещи становятся проще, если предполагать, что у всех функций всегда один аргумент.
Математик и логик Хаскелл Карри ещё в первой половине 20 века ввёл концепцию "каррирования" — превращения функций многих переменных в функцию одной переменной. Эта техника широко используется в языках, поддерживающих функциональную парадигму, например, Haskell и OCaml.
В Standard ML пошли немного другим путём: функции с "несколькими аргументами" на самом деле всегда принимают на вход один аргумент — кортеж.
mayorovp
16.02.2017 13:57+2Каррирование к тому, что обсуждается в этом посте, не имеет никакого отношения. Оно никак не упростит то же "программирование процессов мышкой".
Eldhenn
16.02.2017 14:00-3Если у функции больше двух аргументов, стоит задуматься о передаче хэша/словаря/таблицы.
MacIn
16.02.2017 17:59-1Из крайности в крайность: четыре внятных аргумента вида width height и так далее, которые влазят в экран и имеют говорящие имена, лучше, чем ватное opts, на мой взгляд; с другой стороны — десять аргументов — это уже нездорОво.
flancer
16.02.2017 18:03-1Виной всему — моя лень. Мне было лениво расписывать "нездоровый" вариант на десять аргументов, и я остановился на более коротком, надеясь, что идея будет понятной. В результате запорол пример. Ну что ж, буду работать над собой.
devpony
16.02.2017 20:09+3Ваша проблема на самом деле связана с типами. Меняя порядок следования аргументов в динамическом языке вы неявно меняете тип функции. В хорошем статическом языке такая ситуация невозможна. Предвосхищая замечания, для типов, одинаковых на машинном уровне, но разных семантически, существует
newtype
.
Передача вместо множества аргументов одного большого объекта, конечно, решает проблему с порядком аргументов, но рождает проблемы ещё больше и серьёзнее.
am-amotion-city
16.02.2017 20:40-1В хорошем статическом языке такая ситуация невозможна.
Ну, давайте, расскажите, как ваша Панацея Статической Типизации поможет в случае перепутанных
width
иheight
в функции, принимающей всего-навсего два параметра.
Хайп он такой хайп.
devpony
16.02.2017 20:53+4newtype 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
am-amotion-city
16.02.2017 21:04-5OMG.
Все, все, ухожу. Представляю, какая космическая скорость разработки у вас, если на каждый чих нужно пару типов объявить.
Да и данные, что греха таить, никогда не приходят из внешнего мира, они же только в абстрактных академических примерах из пальца высасываются. И в базе так же лежат, дайте угадаю, сериализованные вместе с типами, да?
devpony
16.02.2017 21:31+4Все, все, ухожу. Представляю, какая космическая скорость разработки у вас, если на каждый чих нужно пару типов объявить.
Мне — не нужно. У автора поста возникла проблема. Я утверждаю, что фундаментально это проблема не синтаксиса, а типизации и показываю как бы автор мог её решить на статически типизированном языке.
И в базе так же лежат, дайте угадаю, сериализованные вместе с типами, да?
Ну можно и с типами, если очень хочется. Правда не совсем понимаю, какое это отношение имеет к обсуждаемой теме.
am-amotion-city
16.02.2017 22:01-3Наипрямейшее. Автор говорит: «Смотрите, как можно упростить себе жизнь в джаваскрипте». Приходите вы, и говорите: «Проблема в языке, нужна статическая типизация».
Во-первых, эта самая статическа типизация очень сильно перехвалена, но это ладно. Просто это тут вообще ни при чем: любая задача решается в рамках входных условий, а тут входное условие — джаваскрипт.
Например, потому что у нас фронтенд весь (такие мы осталые) на джаваскрипте. И переписывать его на Elm мы не станем. Или еще почему.
И «ваш язык дерьмо» — крайне непрофесиональный подход. Задачи нужно решать в реальном мире, а не в хипстерском вакууме.
HKA
20.02.2017 16:51+1По теме «космической скорости»: использовали бы статическую типизацию для дюймов и метров, глядишь, Mars Climate Orbiter по-прежнему бороздил бы просторы
Большого театра.
mayorovp
16.02.2017 22:11-2… а теперь напишите функцию, которая будет поворачивать изображение, пользуясь "типами" высоты и ширины...
devpony
17.02.2017 02:30+2data Image = Image Width Height [[Int]] rotate :: Image -> Image rotate (Image (Width w) (Height h) xs) = Image (Width h) (Height w) $ transpose xs
am-amotion-city
17.02.2017 08:50-1Прямо ведь невооруженным глазом видно, насколько это круче, внушительнее и проще в сопровождении, чем:
h, w = w, h
am-amotion-city
17.02.2017 13:03-5Для минусующих религиозных фанатиков и просто дебилов поясню: хорошему программисту типы в 99% только мешают, а плохого программиста — никакие типы не спасут.
Кроме сильного глазного кровоточения при взгляде вот на этот кошмар с высотой и шириной выше, этот подход не только не приносит никаких ощутимых плюсов, но и просто нежизнеспособен в реальном мире, в котором данные живут не в изолированном сферическом вакууме.
Именно поэтому в хорошо продуманных языках, зарекомендовавших себя в продакшене, используются стоящие особняком инструменты проверки типов, наподобие
dialyzer
в эрланге. Они помогут отловить ошибки, но не заставляют разработчика танцевать с арбузом вприсядку, если функция вдруг легитимно полиморфна.flancer
17.02.2017 13:52+1Весьма категоричное заявление, коллега. Причем в нем не охвачены средние программисты, к коим я и себя отношу и коих по моему мнению — большинство. Мне, например, типизация (если она есть) весьма помогает ориентироваться не только в чужом коде, но и в собственном тоже (разумеется, при поддержке IDE). Не спасает, но и не мешает.
am-amotion-city
17.02.2017 14:05-5А я вообще весьма категоричный хрен.
И обсуждать нужды средних программистов мне тоже как бы недосуг: это как обсуждать стихи средних поэтов, интересно только автору. Помогает вам IDE — пользуйтесь на здоровье. Чтобы оно, это ваше IDE, разобралось в типах, — языку типизация вообще не нужна, как я, собственно, уже сказал в предыдущем комментарии. Необязательных указаний типа для IDE достаточно.
А вот что касается живого кода — нафиг там никому типы не вперлись. Потому что это мое дело, а не компилятора, что я хочу передать в функцию. Полицейское государство в рамках проекта строить неосмысленно. Но хайп сейчас развели про то, что осмысленно, вот дураки и ведутся.
flancer
17.02.2017 14:09Чтобы оно, это ваше IDE, разобралось в типах, — языку типизация вообще не нужна
Объяснитесь, пожалуйста. Если досуг, разумеется.
am-amotion-city
17.02.2017 16:18-3Я же уже все написал: посмотрите, как
dialyzer
в эрланге сделан: аннотация типов. Вы просто описываете типы в критичных местах (или везде, если время девать некуда), как хотите, например: «вот эта функция принимает функцию, туплу, или строку, начинающуюся с "http://"», и пускаете в вашем CI среди прочего иdialyzer
.
Так Elixir собирается, например, а у него в
Stream
там вообще весело, половина имлементаций (самые старые) — возвращают каррированную функцию с arity 2, а вторая половина — умные структуры, которые умеютreduce/2
. И ничего, IDE живее всех живых все понимает, из-за аккуратно аннотированных типов.
А жесткая типизация приводит к тому, что изменилось стороннее API, данными которого мы пользуемся — и добро пожаловать в ад рефакторинга.
mayorovp
17.02.2017 16:29+4Зато без "жесткой типизации" изменилось стороннее API, данными которого мы пользуемся — и вместо падения сразу вся база оказывается забита строками "undefined" на месте обязательных колонок :)
am-amotion-city
17.02.2017 17:32-4Слушайте, ну вот зачем вы глупости-то говорите, а? Я где-то предлагал отменить валидацию данных? Между валидацией и жесткой типизацией — пропасть же. А вот эти с хайпом в голове — придут, почитают, и решат, что хороший код писать не нужно, достаточно очень жестких типов.
Забавно смотреть, как диванные аналитики, в жизни до продакшена самостоятельно с нуля ничего не доводившие, минусами разбрасываются. Я бы тоже, может, и рад бы на каждый чих тип завести, да описать его в документации на пятьсот строчек, да вот как-то так получилось, что КПД этого процесса в реальном мире отрицательное.
Критичные части я аннотациями и тестами обложу, а если кто где мне вместо ожидаемого пришлет что-то не то, так паттерн-матчинг гораздо эффективнее строгой типизации эту задачу решает: процесс помрет просто и все, да новый народится.
При этом мой неакадемичный подход заодно и строчку
"undefined"
вместо"http://example.com"
развернет, потому что я сигнатуру функции буду матчить по префиксу, а прекрасные типы схавают и не поперхнутся. И вся база заполнится — сюрприз — андефайндами.
Да вот только в общем случае и это не спасет, и ничего не спасет, кроме растущих из нужного места рук и тренированного головного мозга. Но если ни того ни другого нет, может и жесткая типизация помогает, тут не могу судить за недостатком эмпирических данных.
mayorovp
17.02.2017 19:14+3Вот-вот. Вам нужны аннотации, тесты, CI, валидация… А мне достаточно прописать пару типов!
При этом мой неакадемичный подход заодно и строчку "undefined" вместо "http://example.com" развернет, потому что я сигнатуру функции буду матчить по префиксу, а прекрасные типы схавают и не поперхнутся.
Существует такой тип как
System.Uri
.
basili4
17.02.2017 18:17+1Не могу плюссануть оставлю коммент с неистовым плюсом.
Порой уж лучше бы умерло сразу. Чем через пару дней увидел, что вместо цифер, стали прилетать буковки.
flancer
17.02.2017 18:19+1аннотация типов
Получается, что самому языку типизация не нужна, но чтобы IDE могло в типах разбираться все-таки в каком-то виде (аннотации) эти типы указывать нужно.
А я вообще весьма категоричный хрен.
Вы полностью подверждаете свою собственную характеристику. Причем во всех частях. Похвальная самокритичность.
am-amotion-city
17.02.2017 20:15-3Пока получается только то, что вы либо читать не умеете, либо понимать прочитанное, либо и то, и другое вместе.
IDE пользуются далеко не все: раз. Без IDE и клик-клик мышкой программировать не умеем, или это критичная часть? — вот аннотации, пожалуйста. Рекомендательные, еще раз повторяю. Компилятор их вообще не видит. И не должен.
Потому что если я хочу, чтобы процесс упал и возродился — я просто напишу хендлеры на все понятные случаи входных данных и все. Получили бяку? — не волнует, хендлер не заматчится и процесс рухнет. Штатно рухнет, и супервизор его перезапустит. Вместо пяти экранов защитного кода.
Зачем в такой парадигме нужна принудительная проверка типов — я отказываюсь понимать, считайте, что ввиду упертости и узости мышления.
michael_vostrikov
18.02.2017 10:19+2Вы правда не понимаете или прикидываетесь?
языку типизация вообще не нужна
— Эрланг это язык со строгой динамической типизацией.
Именно она помогает паттерн-матчингу и предохраняет от большинства ошибок.
— Указание типа это один из вариантов паттерн-матчинга
В Эрланге например можно написать так:
f(X) when is_integer(X) -> ...
В других языках указывается тип аргумента:
f(int x) { ... }
хендлер не заматчится и процесс рухнет
— В Эрланге есть процессы, которые встроены на уровень языка.
В отличие от многих других языков, где потоки создаются средствами ОС.
Вместо пяти экранов защитного кода
Надо написать пять экранов паттернов.
я просто напишу хендлеры на все понятные случаи входных данных и все
Это в любом языке так — написали хендлеры и все. Другое дело, что в Эрланге проверки можно описывать декларативно, и среда сама выкинет исключение если что. И да, если указано, что функция принимает на вход int, а в рантайме туда передается object, то и в других динамических языках выбрасывается исключение, и процесс так же падает. Если конечно нет специального обработчика, который его поймает.
DarkEld3r
17.02.2017 17:13+4обсуждать нужды средних программистов мне тоже как бы недосуг: это как обсуждать стихи средних поэтов, интересно только автору.
Безотносительного всего остального, но эта аналогия совсем не к месту. Стихи — творчество и тут естественно выбирать лучшее, а вот в программировании работы хватает, так что задействованы не только "средние", а и "слабые" программисты. И инструменты позволяющие чего-то добиться имея слабую/среднюю команду однозначно интересны. Опять же, безотносительно типизации.
flancer
17.02.2017 18:10+1Полностью согласен, программирование — это craft, а не art. Воспроизводимость, дополняемость, инженерность, если хотите, здесь важнее уникальности или самобытности. Понимание этого приходит со временем и не ко всем.
am-amotion-city
17.02.2017 18:38-2в программировании работы хватает, так что задействованы
не только «средние», а и «слабые» программистыИ что? Ну, обкрадывают эти люди доверчивых гуманитариев, которые убеждены, что программирование — это рокетсайенс (а на самом деле — ремесло, не сложнее плотницкого, а во многом — гораздо проще).
Имея слабую/среднюю команду, имеет смысл эту команду обучать, а не топор точить.
DarkEld3r
17.02.2017 18:55+2Слабой команде можно платить меньше. Если язык позволяет с такой командой добиваться результата, то это вполне возможная стратегия.
flancer
17.02.2017 19:17+2имеет смысл эту команду обучать, а не топор точить
И. Не "а", а "и". Имеет смысл и инструментарий содержать в порядке, и навыки его использования нарабатывать. Вы, дружище, бросаетесь в крайности, пролетая мимо золотой середины.
Sirikid
18.02.2017 23:44+2h, w = w, h
А как же пиксели? Нельзя просто так взять и поменять высоту и ширину местами.
… а теперь напишите функцию, которая будет поворачивать изображение, пользуясь "типами" высоты и ширины...
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
.am-amotion-city
19.02.2017 10:16-1Это академический кошмар, который никогда не попадет в продакшн. Поймите, в теории это все выглядит очень красиво, даже я это признаю?.
Но на практике мы непременно захотим, скажем, масштабировать по длинной стороне до 600px (если ландшафт — 600 ширины, если портрет — высоты.) Или еще что (не нужно выкладывать очередную простыню изящного на ваш взгляд кода, я верю, что все возможно).
И смысл в том, что такие задачи появляются как бы три раза в день. Ширина и высота — это одна и та же сущность, описываемая в реальном мире одним и тем же: метрикой, взаимно однозначно отображающейся на множество целых положительных чисел.
Тут можно навертеть 6 слоев красивых академических абстракций, но при наличии минимального интеллекта у разработчика, это все избыточная ерунда. Может быть, неофитам помогает, не знаю.
Sirikid
19.02.2017 20:33+1И смысл в том, что такие задачи появляются как бы три раза в день.
Не вижу проблемы, Haskell позволяет вообще не писать типы, писать типы для топ левел функций просто правило хорошего тона. То же можно сказать про многие ML-подобные языки.
Ширина и высота — это одна и та же сущность, описываемая в реальном мире одним и тем же: метрикой, взаимно однозначно отображающейся на множество целых положительных чисел.
Если ширина и высота выражаются одним и тем же типом это не значит что семантика у них одинакова, когда я создаю псевдонимы
Width
иHeight
я отражаю эту семантику.
Но на практике мы непременно захотим, скажем, масштабировать по длинной стороне до 600px (если ландшафт — 600 ширины, если портрет — высоты).
И эту семантику можно отразить в типе.
Может быть, неофитам помогает, не знаю.
Насколько я знаю неофитам как раз проще с динамической типизацией (но строгой, а не как в JS, лол).
это все избыточная ерунда
А ещё можно сгенерировать более эффективный машинный или байт-код на основе "этой ерунды".
Я понял вашу позицию. Жду ответа по поводу семантики, вывода типов и эффективности.
am-amotion-city
19.02.2017 20:55-1Жду ответа по поводу семантики, вывода типов и эффективности.
Императивы редко помогают конструктивной дискуссии.
это не значит что семантика у них одинакова
Строкой выше я написал, что это одна и та же сущность, что в переводе означает: семантика одна и та же. Удивлен, что вы не предлагаете заводить типы
WidthOfSelfiePictureMadeOnMyLastBirthday
: семантика-то тут явно глубже, чем у простоWidth
.
ещё можно сгенерировать более эффективный машинный или байт-код на основе «этой ерунды».
Это вашей барышне рассказывайте, она поверит. В теории можно. А на практике покажите мне распределенные high-load вычисления с вводом-выводом на Хаскеле. Premature optimization — гигантское зло. Сначала надо уметь взлетать, а потом уже оптимизировать.
Sirikid
19.02.2017 21:26+2Удивлен, что вы не предлагаете заводить типы
WidthOfSelfiePictureMadeOnMyLastBirthday
: семантика-то тут явно глубже, чем у простоWidth
.Ну зачем так, я просто не хочу перепутать два параметра, информация про селфи и день рождения в данном случае не на моем уровне абстракции.
А на практике покажите мне распределенные high-load вычисления с вводом-выводом на Хаскеле.
Императивы редко помогают конструктивной дискуссии.
Это был не императив, честное слово. По крайней мере я хотел вложить другой смысл, что мне интересно услышать ваш ответ.
patricksafarov
16.02.2017 22:08+1в последних стандартах js последний пример еще красивее пишется
function showWarning({ width = 200, height = 100, title = "Предупреждение", contents = "Ничего серьезного." }) { //... }
AlexTest
17.02.2017 03:33
Судя по примеру на JS — вас не волнует, что там реально предается в opts, тогда вместо ваших «отдельных классов» просто используйте в PHP для тех же целей stdClass что-то типа такого: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 приходится платить разработкой структуры входных/выходных аргументов в отдельных классах. В основном получается муторно, но иногда — весело.
$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 ?? "Ничего серьезного."; //... }
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 ?? "Ничего серьезного."; //... }
AlexTest
17.02.2017 05:10Но вообще, такой подход к вводу параметров/выводу результатов — это даже не BDSM, это гарантированный отстрел всех ног да и рук тоже :)
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).
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) {}
mayorovp
17.02.2017 08:38А чем вариант
@param {ScreenOptions} opts.screen
плох?flancer
17.02.2017 09:40У меня очень положительные впечатления от использования java packages & PHP namespaces, а вот в JS я подобного механизма не вижу. Когда PHPStorm пытается предложить мне автодополнение к началу какой-нибудь JS-функции, то он сперва долго "шуршит мозгами", а потом выдает сразу список всего, что нашел, причем одноименных функций из разных источников там столько, что автодополнением для JS'а я, например, пользоваться не могу. Возможно дело в том, что я работаю с Magento, а там куча не только своего кода, но и и кода из сторонних модулей, со своими JS-зависимостями. Иногда приходится из 5 JQuery-библиотек, которые тянутся модулями, оставлять одну посвежее, а остальные глушить (правда это для М1 было, с двойкой пока такого опыта не было).
Надо будет попробовать ваш вариант на небольшом, отдельном проекте и понаблюдать за. Боюсь, правда, разбор всего содержимого каталога vendor вызовет примерно такой же эффект — куча одноименных функций без возможности идентификации источника кроме как по имени файла (даже без пути). Но если давать "кучерявые" имена для своих структур (VendorProjectModuleClass — чтобы у IDE даже сомнений не было, что я имею в виду), то такой вариант, я думаю, вполне даже хорош.
lair
Добро пожаловать в функциональное программирование.
flancer
Поражаюсь вашей скорости чтения, коллега.
customtema
У меня тоже заняло секунд 20. Наверное потому, что я давно и много занимаюсь и программированием, и чтением блогов. В чем проблема?
flancer
У вас тоже? Вы с коллегой lair'ом там что, на скорость чтения соревнуетесь? В таком случае примите от меня респекты и в свой адрес, коллега.