программерское :: oop & literal programming
Правильно я думал, что предыдущий пост вызовет вопросы : ) Он их вызвал. Обьяснюсь, чего я хочу и почему. Я хочу двух вещей: объектно-ориентированного подхода и повышенной читабельности программы. Эти вещи друг с другом очень связаны, поэтому ниже я не особо буду их разделять.
Возмущение первое связано с порядком аргументов в глобальных функциях и противоестественностью для живого человека принятой в таких случаях записи. В известных мне человеческих языках порядок частей речи чаще всего такой: субъект
, действие
, объект
. Субъект
производит действие
над объектом
. Субъект
(в отличие от многих других субъектов) умеет производить определённое действие
, причём оно должно производиться над объектом
. Переходя к программированию, логично сделать действие
методом субъекта
, и этот метод будет принимать параметром объект
. И программист радостно напишет что-то вроде субъект.действие( объект )
или палец.нажимает( кнопкаМыши )
. Напишет... в счастливом будущем. А сейчас в одном многими (справедливо?) нелюбимом, но очень популярном языке программирования ему приходится писать нажимает( палец, кнопкаМыши )
или нажимает( кнопкаМыши, палец )
— каждый раз в разном порядке. Ну да ладно, это свойство языка, и кое-где солнце светит ярче.
Возмущение второе связано с необходимостью делать так:
$o1 = new Order();
$customer->addOrder($o1);
$line1 = new OrderLine(6, Product::find(‘TAL’));
$o1->addLine($line1);
$line2 = new OrderLine(5, Product::find(‘HPK’));
$o1->addLine($line2);
$line3 = new OrderLine(3, Product::find(‘LGV’));
$o1->addLine($line3);
$line2->setSkippable(true);
$o1->setRush(true);
и невозможностью делать вот так:
$customer->newOrder()
->with(6, ‘TAL’)
->with(5, ‘HPK’)->skippable()
->with(3, ‘LGV’)
->priorityRush();
Ну вы видите, да? Второй вариант можно читать. Это почти человеческий язык. Кое-то называет это "Fluent Interfaces", а кто-то — удобным api, но суть не в этом. Суть в том, что это читается почти как предложение (отсюда, кстати, literate programming в заголовке).
А возмущение третье связано с синтаксисом оператора отрицания. Вот что вам приходит в голову при виде записи !string.contains( 'substr' )
? Если вы — не машинноговорящий франкенштейн человек, то это нужно читать как «не строка содержит подстроку
». Ага! «Йоды магистра речи тайна раскрыта, оказывается, на форте программист старый есть он просто.» Нормальные люди (кроме немцев : ) так не говорят. У них отрицание ровно перед действием стоит: «строка не содержит подстроку
». При переводе в машинный язык это должно выглядеть как string.!contains( 'substr' )
. Но не выглядит ни в одном из известных мне языков программирования : (
комментарии
__ronin__:
Первая мысль - для упрощения труда создателей компиляторов. А дальше надо поразмыслить...
_arty:
создателей компиляторов это не особо напрягает, потому что это банальная задача парсинга, а после этого идут гораздо более сложные и интересные вещи
я так думаю
__ronin__:
то есть, даже на стадии семантического анализа нет никаких проблем?
_arty:
я не буду говорить, что так оно и есть, но глубоко уверен, что так оно и есть : )
__ronin__:
На самом деле, если подумать, проблем действительно нет - к данной стадии, при выносе отрицания за скобку никаких проблем не возникнет. Единственно, что не могу сейчас проверить, это наличие более сложных семантических случаев, неоднозначно трактуемых в условиях такого синтаксиса - мозг не тем занят :)
rageous:
если правила можно формализовать, то никаких проблем не должно быть
__ronin__:
Да, верно, я потом подумал
rageous:
честно говоря, как я ни ломал голову над вторым вариантом, так и не смог найти способа, как его можно прочитать мб я и есть тот самый "машинноговорящий
франкенштейнчеловек" :)_arty:
ну блин, по частям
подстроку
rageous:
не, я не про строку, а про кастомеров и ордер-лайн
_arty: опа, промахнулся
ну да, там не очень приятный пример, к сожалению, но даже он более-менее читается
клиенту - новыйЗаказ
на шесть TAL'ов,
на пять HPK'ов ( можно пропустить ),
на три LGV,
оченьоченьСрочный
rageous: да это я виноват :)
в с++ можно сделать похожую штуку - будет что-то типа потоков:
client.newOrder()
<< entry("TAL", 6)
<< entry("HPK", 5).skippable()
<< entry("LGV", 3)
<< priorityRush();
"<<" можно заменить на другой символ: "+", например....
_arty: Re: да это я виноват :)
да, как раз на cout я уже такое видел : )
это ещё называют method chaining
rageous:
угумс :)
uburwator:
мне, кстати, первый вариант логичнее показался.
потому что с трудом представил, как можно отрицать оператор... для данного-то примера понятно, а вот для чего-то более сложного...
_arty:
помни, ты не оператор отрицаешь ; )
uburwator:
Я-то помню :)
Вообще, просто всё дело привычки ;)
_arty:
привыкнуть можно вообще к чему угодно
но лучше привыкать к хорошему : )
rageous:
"Возмущение первое связано с порядком аргументов в глобальных функциях"
могу лишь заметить, что глобальные функции типа "void push(Button, Finger)" - это не ооп :)
_arty:
естественно не ооп : )
просто php славится глобальными функциями : )
rageous:
ну это уже косяки конкретного языка... :)
_arty: ; )
rageous:
ммм... сорь, не заметил :)
quappa:
По пунктам:
1. Возразить нечего. Плохой язык, плохой. Лишить воскресной чарки два раза :) Как же у них, интересно, полиморфизм работает, неужели по первому аргументу? Я не трогал PHP с версии 3 и поэтому не очень в курсе.
2. Такой синтаксис сплошь и рядом применяется в C++ и Перле. Вот перловый пример (который будет поинтереснее, чем банальный чейнинг шифтами в iostream-ы): http://cpan.uwinnipeg.ca/htdocs/SOAP-Lite/SOAP/Lite.html
3. Прикинь, в MIT-е программировать учат на языке с префиксной нотацией :) Которая вообще на естественном языке не читается никак. Что-то в этом есть, мне кажется. А с прагматической точки зрения очень хорошо тебя понимаю. Ровно поэтому в Перле есть два оператора мэтчинга регекспов: =~ и !~. И наряду с it и while имеются противоположные им unless и until.
Я выгляжу как агитатор. Мне почти стыдно. Извините, но оно так всегда выходит с этим языком на букву Пё :)
_arty:
1) вообще он работает, просто не для всего. Например, строки и числа о нём не знают (поскольку не объекты), а функции работы с ними используются очень часто.
2) да, видел в перле такие методы, но только сейчас осознаю, для чего они были сделаны. А в примерах в мануале классы постоянно хэшами инициализируются : ( Во всяком случае, те, которые я обычно видел.
3) не надо тут извращённых нотаций : ) Я понимаю, что кое-что ум в порядок приводит © и так далее, но после развития некоторой гибкости оного постоянно использовать странное не хочется.
4) в перле сейчас с объектами все-таки туго. Верю, что лучше, чем в пхп, но строки, числа и массивы по-прежнему не объекты. Javascript в этом плане жжот.
node.firstChild.nodeValue.match( /test/i )
Да, я знаю, что в шестом перле с этим получше.
quappa:
1) Нет, стоп, методы у объектов действительно вызываются синтаксисом method(obj, args)?? Я правильно понял?
...читается тоже почти как песня :) Хотя на самом деле представляет из себя вызов метода Ford::Focus::new с передачей хэша.2) Чейнинг затрудняет проверку ошибок. Очень :) Приходится глобально переходить на эксепшэны, а они на практике не всегда применимы. Это один из контраргументов. С другой стороны,
3) ... это ты по молодости :))) Префиксная нотация таит массу приятных сюрпризов. Лисп -- это высококалорийная вкуснятина в невзрачной упаковке.
4) Яваскрипт вообще удивительный язык. Я ему готов дифирамбы петь, но ты, видимо, и сам всё знаешь. И в то же время пример ты привёл неудачный, противоречащиий логике оригинального поста. str.length
_arty:
1) при желании, да. А вообще методы объектов вызываются именно obj->method(args). Другое дело, что стандартных объектов маловато будет. Бог бы с ними, с литералами, но для доступа к мускулю можно было бы объект использовать. А для массивов сколько там методов типа array_pop — ужас!
2) есть такая партия. Да, только эксепшены. К сожалению, у меня мало опыта про них, так что не могу сказать ничего умного. А вот просто, что перлу хэши можно именно так скармливать, не знал : (
3) а вот некоторые знающие люди говорят, что руби с его человеческой нотацией - прямой наследник лиспа в том числе. Красивая упаковка, кстати, тоже бывает полезна : )
4) JS хорош, да : ) Некоторые, правда, про питон говорят, что это javascript made right (или как-то так : ) Что касается длины строки, то это только в русском плохо получается. В английском наоборот - "string length" - вполне естественная конструкция. И насчёт регэкспа ты ошибся:
string.match( /test/i )
/re/i.test( string )
Для накладных расходов есть новые процессоры, а то вон, юзеры уже не жалуются на компы трёхлетней давности ; )
Плюсы от обектности литералов бывают, например, вот такие:
40.days.from_now + 2.hours => Thu Jan 05 05:14:59 CET 2006
quappa:
3) Мм. Я не знаю языка (ну кроме фортрана с ассемблерами), который бы не унаследовал чего-нибудь из Лиспа :)
4) В питоне уродские лямбда-функции и нет прототипного ООП. То есть самые сильные стороны Яваскрипта там отсутствуют, по моему скромному мнению.
Да, перепутал match, каюсь. Мало пользуюсь. Ты знаешь, производительности мозиллиного яваскрипта на Athlon 3500 мне реально не хватает. Настолько там всё виртуализовано и абстрагировано.
К нам, кстати, взяли моду ходить с телефонов. Там на новые процессоры расчитывать не приходится, особенно если браузер сделан в виде мидлета на яве :( Я уже, честно говоря, отчаялся дожить до времени, когда про оптимизацию по скорости можно будет спокойно забыть.
О, ещё один " is Javascript done right" :))) Вспоминается древний форчун: The statement "X is one true Y" is false for every pair (X, Y) where X != Y.
40.days.from_now выглядит неплохо, хотя и хак. И в принципе раскручиваемо на этапе компиляции, поэтому не приведёт к оверхеду. Интересно, хотя и подмывает спросить, откуда оно узнало часовой пояс. А также что будет в случае user_var.days.
_arty:
Мозиллин яваскаскрипт действительно небыстр. Опера в этом плане жжёт.
а в телефонах яваскрипт насколько хорошо работает? Я вот наслышан, что в opera mini его вообще нет.
про часовой пояс можно узнать оттуда же, откуда узнают все никсовые программы - из переменной среды : ) А пользовательскую переменную можно скастовать в число (или вначале в строку, потом в число), и результат уже преобразовывать в дату.
quappa:
В Opera Mini он есть, во всяком случае alert(document.cookie) у меня работает.
_arty:
понятно
странно, какое-то недавнее обсуждение аякса говорило, что события там странно реализованы
quappa:
Пардон, s/it/if/
anonym:
$customer->newOrder()
->with( new OrderLine(...) )
->with( new OrderLine(...)->skippable() )
->with( new OrderLine(...) )
->priorityRush();
по-моему, можно нарисовать в ПХП и, если возвращать "правильные" значения, это даже будет работать.
В твоем примере не понятно почему priorityRush() связанный с with() также как и skipabble() относится не к строке заказа, а ко всему заказу. Если пробельные символы не игнорировать, а тем более различать перевод строки и пробел, то замучаешься грамматику рисовать.
string.!contains(substring) - попробуй прочитать на естественном языке, учитывая что точка и скобки - это не знаки пунктуации, на которые можно забить, а операторы, обознащающие определенные действия (доступ к методу и вызов).
oslikovod:
это были мы
_arty: