Pull to refresh

Comments 67

в файлах заголовка (*.h) декларативным способом описываются структуры данных, функции и методы, а в файлах программы (*.c/*.pp/*.cpp) на императивном языке описываются действия над данными

Всегда именно так?

В .h не бывает императивного кода, а в .c/.cpp описания структур данных?
А то я слышал, что там один и тот же язык и указанные .h просто файлы включаются в .c/.cpp на этапе препроцессинга.

PHP, который делится на HTML часть, описывающий структуру данных для визуализации, и PHP часть, предназначенную для описания действий по заполнению структуры данными.

HTML как часть PHP? Покажете где это в спецификации?

Самый логичный и понятный фреймворк — это фреймворк, предоставляющий минимальный API с максимальным функционалом за ним.

По этому критерию нормальные алгоритмы Маркова логичнее и понятнее, чем, например Django или Rails. Почему же их не используют для разработки приложений?
В .h не бывает императивного кода, а в .c/.cpp описания структур данных?

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

HTML как часть PHP? Покажете где это в спецификации?

Я буду рад, если вы мне подскажите ссылочку на спецификацию PHP :) А, вообще, там, как бы, есть включения между ?> и <?, которые предназначены именно для вывода HTML

По этому критерию нормальные алгоритмы Маркова логичнее и понятнее, чем, например Django или Rails. Почему же их не используют для разработки приложений?

Может быть по тем же причинам, по которым не используют таблицу переходов состояний Тьюринга?
А, вообще, там, как бы, есть включения между ?> и <?, которые предназначены именно для вывода HTML

Они предназначены для вывода чего угодно, не обязательно HTML. Так что именно для вывода HTML (=исключительно для вывода HTML) они не предназначены.
Ок, ок, но скажите мне, для чего создавался язык PHP? Вспомните историческое предназначение этой конструкции.
Историческое предназначение конструкции — переключение интерпретатора PHP в режим pass through.

Вы путаете предназначение конструкции с тем, для чего ее обычно используют. Это, безусловно, пересекающиеся множества, но не равные.
Историческое предназначение конструкции — переключение интерпретатора PHP в режим pass through.

Подтвердите это утверждение, пожалуйста, ссылкой на документацию PHP первых версий :)
В PHP первых (да и вторых) версий данной конструкции не было. Хватит уж юлить, признайте, что ?> <? — это конструкция вывода произвольных бинарных данных (а не HTML) да и закроем эту тему.
Это абсолютно не говорит о том, что HTML — часть PHP. Ваш КО.
Я совершенно не придираюсь к вашим словам, но:
Историческое предназначение конструкции — переключение интерпретатора PHP в режим pass through

В PHP первых (да и вторых) версий данной конструкции не было.


Это раз. Два — где-то написано, что HTML — это часть PHP? Я ещё раз процитирую фразу:
Другой пример — это PHP, который делится на HTML часть (все *ML языки являются декларативными), описывающий структуру данных для визуализации, и PHP часть, предназначенную для описания действий по заполнению структуры данными.
Вы меня немного спутали не со мной, я КО, а не Ваш предыдущий собеседник :)

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

PHP, который делится на HTML часть [...] и PHP часть [...].


Покажите мне тут HTML-часть. Так можно было делать давно-давно в PHP. Я немного увлекся, но это просто чтобы показать, что HTML тут абсолютно не при чем.

(defun factorial (n)
  (cond ((minusp n) (error "Factorial of negative numbers is undefined"))
        ((< n 2) 1)
        (t (* (factorial (- n 1)) n))))

(defvar *numbers* <?php

$count = 3;
$f = function($x) { return "(cons (* (- $x 1) (+ $x 1)) "; };
echo implode("", array_map($f, range(1, $count))) . "nil" . str_repeat(")", $count);

?>)

(format t "#include <stdio.h>

int main(void)
{
	printf(\"%d\", ~D);
	return 0;
}" (factorial (car <?php

echo str_repeat("(cdr ", $count - 1);
echo "*numbers*";
echo str_repeat(")", $count - 1);
?>)))


Два — где-то написано, что HTML — это часть PHP?

Да, в цитате, которую вы сами и привели.
Вы на синтаксис PHP/FI взгляните и найдите там конструкцию <?...?>
Вы придираетесь к словам: <? — > в третьей версии заменили на <? — ?>.
Суть от этого не изменилась: PHP изначально был создан как язык инъекций в язык HTML, что было отражено в его названии (FI == Form Interpreter).

Синтаксис обёртки инъекций специально подбирался с тем, чтобы он не конфликтовал с нативным языком HTML. Видите ли, если вы будете выводить JPEG файл с инъекциями PHP или код C, как в предыдущем примере, вы не будете застрахованны от конфликтов синтаксиса вставок с синтаксисом аутпута между вставок:
никто не гарантирует в JPEG файле отсутствие возникновения последовательности <? (с произвольным набором байтов после этого и падением интерпретатора на них), как и отсутствие в программе на C строки
 printf("<?%s","death of interpreter");


Тоже самое, впрочем, относится и к обычному тексту, не оформленному согласно правилам HTML разметки (XML появился несколько позже первой версии PHP/FI). Для того, чтобы инъекции PHP не конфликтовали с прочим контентом файла, если он не написан на HTML, необходимо было специально учитывать наличие вставок на PHP.

Теперь с удовольствием выслушаю ваши возражения насчёт исторического предназначения конструкции <? — ?> и её практического применения в настоящее время.
Вы придираетесь к словам[...]

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

Видите-ли, программистам свойственна склонность к точным формулировкам (видимо, сказывается воспитание компиляторами, которые тоже довольно строги в этом плане). Поэтому и курс для программистов не должен допускать подобных неточностей. Иначе он производит впечатление неряшливости, неаккуратности — именно того, чего программист в своей работе допускать и не должен. Пренебрежение точностью высказываний в данном случае подобно пренебрежению обработкой ошибок в коде — вроде бы и работает все, для большей части случаев, но доверия такой код не вызывает.
Прекрасно, предложите другую формулировку, пожалуйста.
Что касается PHP, то достаточно точной формулировкой (но все равно упрощенной) мне представляется такая:
Конструкции '<НАЧАЛО ФАЙЛА>что-то<?', '?>что-то<?', '?>что-то<КОНЕЦ ФАЙЛА>' аналогичны конструкции echo 'что-то';, но без обработки эскейп-последовательностей. Собственно, именно в такой байткод оно и компилируется. Упрощением тут является то, что в начале файла может игнорироваться shebang, а в конце файла — перенос строки. Учитывая, что echo 'что-то'; — суть императивная вещь, в качестве примера смешения декларативщины и императивщины ее уже использовать не получится.

Для примера смешения, мне кажется, куда больше подойдет Java — структура классов — вещь декларативная, тела методов — императивны.
и предоставляемого API

С этим я согласен =)

но тип данных в .c файле не опишешь

В .c файлах часто описываются внутренние структуры данных.

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

Да, мне тоже кажется, что тут причина та же.
Но это же такие логичные и понятные фреймворки?

Может быть нужны какие-то еще критерии?
конечно нужны, но это обязательное, а не достаточное понятие для простого фреймворка
По-моему вы придираетесь к словам. Суть изложенного не в скрупулезной точности примеров, а в мыслях на которые наталкивают примеры.
Часто, наводя примеры, человек предполагает, что слушатель будет смотреть на них через некоторое мутноватое стекло, которое прячет детали, но акцентирует внимание на некоторых общих аспектах, возможно идеализируя или искажая некоторые моменты.
>Таким образом, чем больше пар язык-язык используется в программе, чем больше используется структур данных, тем больше программа содержит преобразований.

Такое надо доказывать а не в виде очевидной леммы выставлять.
Например имеем конечный ряд цифр ai, которые преобразуются в bi, по правилу f(a,i), но крайние элемент имеет исключение. При переходе к бесконечному ряду, правило сохраняется, но конечные исключения можно выкинуть.
Данных больше — преобразований меньше

>Фактически, использование одного дополнительного конфига или дополнительного языка программирования добавляет в программу в два раза больше кода

Вообще-то в таких случаях говорят в пи раз больше :)
Нигде не показана равнозначность входящих данных и конфигов или API, поэтому не понятно банальное удвоение сущности, если зависимость не определена и более того не монотонна.
Если входящие данные — частный случай и дополняются конфигами/API/чемугодно до какой-нить полной задачи, то стоит ожидать уменьшения преобразований.

Так что конечный вывод
>Добавление фреймворка, конфига или ЯП для облегчения разбора данных приводит к усложнению разбора данных.
считается высосанным из отдельно стоящей части человеческого тела.
Нигде не показана равнозначность входящих данных и конфигов или API, поэтому не понятно банальное удвоение сущности, если зависимость не определена и более того не монотонна.


Давайте рассмотрим конфиг подробнее. Конфиг — это какая-то структура данных, заполненная данными и вынесенная за пределы программы. Так? Так. Соответственно, согласно самому определению конфига, добавление этого конфига приводит к двум следствиям:
1. мы добавляем к программе новый источник данных (а значит по закону о сумме функционала неизбежно увеличиваем меру функционала материнской программы — программы которая раньше работала без конфига)
2. мы добавляем в материнскую программу знание о структуре данных, которой является конфиг, т.е. косвенным путём добавляем ещё один источник данных, тем самым опять же наращивая функционал программы.

Если сравним минимальное количество правил обработки, которые необходимо написать для добавления информации на языке родном для правил и минимальное количество правил, которое нужно декларировать для описания программы и конфига, увидим, что добавление конфига обходится минимум вдвое дороже, чем без него :)

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

не факт, недоказанность монотонности зависимости функционала от входных данных делает это утверждение недоказанным

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

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

>вдвое
o5 25

>API — это интерфейс программного модуля для получения и отправки данных

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

недоказанность монотонности зависимости функционала от входных данных

Вы, ведь, про монотонное возрастание количества функционала при добавлении источника или стока данных говорите? Это утверждение опирается на определение программы, как списка правил для преобразования данных между источником и стоком информации, а так же на третий принцип термодинамики, утверждающий, что при преобразовании информации мера хаоса не уменьшается, а понижение меры хаоса (его упорядочивания) по сути требует задания новых правил. Ещё вопросы? :)

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

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

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

Мы же рассматриваем функцию возрастания сложности программы, а не функцию накопления, не так ли?
>Ещё вопросы? :)

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

>пример

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

>не так ли?
ага
А причем тут незнание структуры конфига? конфиг, его структура может дополнять входные данные со всей метаинформацией до полного случая


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

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

Давайте разберём ваш пример:
Например имеем конечный ряд цифр ai, которые преобразуются в bi, по правилу f(a,i), но крайние элемент имеет исключение. При переходе к бесконечному ряду, правило сохраняется, но конечные исключения можно выкинуть.

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

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

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

Я говорю про меру функционала, которая может при этом увеличится, а может и уменьшится

> о преобразовании ai в промежуточную форму (бесконечный ряд)

не надо бесконечно ряда — есть функция преобразования, которая не меняется с увеличением размера рядов
тогда ещё раз пример поподробнее
что в нем непонятного?
есть ряд a, который через f преобразуется в b
плюс особые условия на концах ряда
а — вход, b — выход
функционал — f и проверка на граничные условия
Тогда в силе мой предыдущий комментарий про преобразования — неважно какой будет промежуточная форма и имеет ли она вид бесконечного ряда.
эээ

>то сумма правил как минимум не уменьшится

это что ли?
Уменьшиться, потому что граничные условия исчезают.
не понимаю, поясняйте
пока данные из конечного рядя, то функционал программы = f + граничные условия
когда данные из бесконечного ряда, функционал программы = f

итого при увеличении множества входящих данных мы имеем уменьшение функционала программы
Ещё раз, сколько стоит преобразование из конечного ряда + граничные условия (это исходные даннфе) в бесконечный + исключения (это промежуточный формат данных)?
Причем тут это?
сравниваем два почти идентичных случая — конечный ряд и бесконечный. Очевидно что входных данных во втором случае стало больше, очевидно что сложность преобразования при этом уменьшилось. dixi
Вы уверенны, что входных данных во втором случае стало больше? Именно этот момент я и просил вас пояснить и ответа не услышал.

Ещё раз — был конечный ряд. Мы берём делаем его инвариантное преобразование в бесконечный ряд + указатели откуда и доколе.

Но на вход программы всё ещё продолжает поступать конечный ряд.

Соответственно, в программу добавляется функционал по конвертации конечного ряда в бесконечный + добавляем новая структура данных «бесконечный ряд + указатели откуда и доколе».

Я не вижу, где здесь уменьшение функционала?
Ещё раз поясню: сама программа продолжает работать с исходными и конечными данными, она не знает, что о том, что можно произвести данное преобразование. По этому это знание необходимо добавить в программу.
а в с ней поговорите, объясните неразумной, может поймет и одумается :)
вы вообще о чем? еще раз, медленно

множество входящих данных — некий ряд а.
на вход подается аi — число
каждому ai соответствует bi — выход — число
данное соответствие в точности повторяет оператор f(a) для всех аi, кроме граничных в ряде членов — для это просто табличные значения.

конечный ряд очевидно не больше бесконечного, подмножеством которого он является

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

в программе в первом случае две вещи — реализация f(a) и проверка на граничные значения
во втором случае только f(a)

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

Теперь представьте что функционал у вас всегда сумма.
Причем он зависит не от ряда, а от рядов.

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

Хотя с этим в целом согласен:
> При возрастании количества функционала, количество кода увеличивается.
Если, разумеется, мы говорим об идеальной ситуации. В реальной же жизни случается рефакторинг, и количество кода порой уменьшается.

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

> суть любого фреймворка — это предоставление API
[тут началась многодневная дискуссия о том, что же такое есть фреймворк]
Если кратко (опять-таки, сугубо имхо) API — это интерфейс.
Я бы определил фреймворк как способ. Closure — способ, и Django — способ. Внахлест — это тоже способ.
Кстати, цена лаконичности API — гибкость. Иногда это допустимо, с увеличением масштаба — все меньше. Опять-таки, имхо.

Инфраструктура не бесплатна. Стоимость возрастает экспоненциально от сложности.
Всё же рефакторинг — это не обязательно сокращение объёма кода, главное в рефакторинге — это повышение выразительности кода при сохранении его функционала неизменным, что может достигаться и более развёрнутым кодом, как, например, в случае замены «магических значений» именованными константами.
Оно-то, конечно, да, но я в данном случае о чуть другой ситуации, когда надо дописать чего-нибудь, открываешь класс, а там… Если время находится, то бывает что удаляешь кода больше, чем добавляешь, хотя и функционал добавляется.
Про конфиги хочу присоединиться и добавить:

Это, соответственно, не добавит счастливости пользователю.

Автору удобно каждый раз, запуская, настраивать программу заново, например, IDE, или всё-таки с конфигами он будет более счастлив?
Я не против конфигов, я против их использования там, где не надо.
По-моему, кто угодно против использования чего угодно там, где не надо. Другой вопрос, по какому критерию Вы предлагаете определять, где надо, а где не надо? Конкретно касательно применения конфигов?
Я, обычно, использую принцип оптимума Паретто по метрикам стоимость разработки, стоимость модификации для неимплементированных требований, частота изменения конфига, количество разных форматов конфигов и сложность изучения структуры конфигов.
(nazi mode on)
Я бы порекомендовал добавить это в статью, потому как в текущем виде она выглядит как «конфиги зло, и фреймворки тоже зло». Согласен, что сама по себе тема стоимости достойна отдельной лекции как минимум, но обозначить границу разумного и неизбежного определенно имеет смысл.
(nazi mode off)
UFO just landed and posted this here
спасибо за комментарий, добавлю к курсу
Никогда не понимал, почему SQL относят к декларативным языкам. CREATE TABLE, INSERT INTO, SELECT FROM — чёткие команды на выполнение, с влиянием на результат последовательности вызовов, то есть с хранением состояния между командами.
Как это не понимали?

Потому что вы описываете не алгоритм получения.
А что вы собственно хотите получить. Этого недостаточно?
Когда я пишу $a = 1; я тоже описываю, что я хочу получить
А процесс выбора из набора данных как у вас выглядит?
UFO just landed and posted this here
Интересно было почитать, но раз уж претендуете на наукообразное изложение, то не используйте сленг («функционал» вместо «функциональность») — глаза режет.
Если с помощью фреймворка можно делать одну и ту же вещь несколькими разными способами, это означает что декларативный язык API этого фреймворка является избыточным.
Это если больше не собираетесь применять этот фреймворк. И не факт, что совсем одну и ту же. А так — ХЗ зачем ещё они завтра понадобится, надо иметь запас.
Насчёт конфигов как недо-ЯП — вы же работаете в Java, там этих декларативных конфижных мини-языков — вагон, особенно в вебе, и все их учат и никто не жалуется.
> Другой пример — это PHP, который делится на HTML часть (все *ML языки являются декларативными), описывающий структуру данных для визуализации, и PHP часть, предназначенную для описания действий по заполнению структуры данными.

Большего бреда я не слышал в жизни.
Sign up to leave a comment.

Articles