All streams
Search
Write a publication
Pull to refresh
93
0
Юрий @m36

User

Send message
согласен.

Если что, то на том диалекте, что я работал, есть и ООП (классы, экземпляры) и возможность писать в императивном стиле.

Хотя, предрасполагает писать всё таки в родном стиле, потому как сильные стороны там проявляются
«Это значит, что и его “высокая читаемость» высока только в этой области»

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

И DSL, конечно, очень полезны. Кстати на нем и какое-нибудь DSL вполне можно писать.

Я только о том говорю, что это язык общего применения. Также как и шарп. Он не заточен под выполнение чего-то. Сама нотация предрасполагает его использовать для математических задач и для обработки больших массивов.
«В устах человека, который на APL пишет, это звучит предвзято.»

Да. И что он более быстрый в разработке, слышал от других, пишущих на АПЛ (я не на АПЛ работал, а на одном из его диалектов). Потом, просто есть личный опыт, потому что я работал и с другими языками. Сейчас шарп и sql.
Тут или проверить можно или на слово верить. Где статистику взять, не знаю. И проводили ли исследования вообще, в виду, не очень большой распространенности этих языков, тоже вопрос.

«Т.е., в конкретной задаче, под которую они оптимизированы.»

Они не заточены под задачи. Там принцип работы другой. Само мышление меняется при работе с ними. Главный принцип работы с ним, что нет никаких привычных методов, а есть лишь то, что мы называем операциями в выражениях. Унарными или бинарными. И единица данных — N-мерная матрица. Поэтому и операции все — преобразования матриц. Вы не пишете циклы, а только превращаете одни матрицы в другие.

Конечно, это хорошо само по себе для сложных алгоритмов и обработки больших объемов. Написать же обработки событий и воркфлоу бизнес-приложения — тоже наверное не сложно. Но скучно.
«А можно ссылку на реальные исследования?»

нет. Личный опыт и слухи.

«Применительно к реальным бизнес-задачам широкого спектра применимости?»

Тоже не знаю, где такую статистику взять. Эти языки очень хорошо применяются для математических задач, финансовых расчетов, сложных алгоритмических задач — обработки большого объема данных. Людей, пишущих на этих языках немного, не слышал, чтобы их нанимали на другие задачи, вроде ГУИ и бизнес-приложение.
Но это языки общего применения. Писать можно что угодно. Вот только более сильные стороны проявляются при работе с большими объемами данных.
«Цикломатическая сложность — «счётное число линейно независимых маршрутов через программный код»»

Наверное. Но во всех этих объяснениях строится граф только учитывая структуру одного метода. Т.е. в простом случае исследуется структура кода (ифы, свитчи, циклы).
Элементарная проверка в студии показывает, что при вынесении чего-то в метод — цикломатическая сложность растет.

«Я уже понял, что APL — король всех языков. Но “более мощный язык», в котором не надо давать имена входным параметрам — это круто, да. Верх читаемости.»

Не поверите, но я высказываю не только свое мнение. Теоретически обоснованное мнение. Если не ошибаюсь, в том же Харрисоне упоминаются такие языки. Не отрицайте очевидное — анонимные методы и лямбды в шарпе именно тем и хороши, что позволяют не вводить ненужные типы (имена), а на ходу сооружать что-то без имени и передавать куда-то.
APL в этом достигает вообще максимума среди мне известных. Нет даже имен у параметров. И это увеличивает читаемость. Вас пугают значки? Да, выглядит это для незнакомых с языком — странным и ломающим мозг. Но то, что в несколько символов описаны сложнейшие алгоритмы (причем реально, а не спрятаны за именами), делает его более читаемым. Повторюсь, читаемость не стоит понимать, как способность прочитать страницу кода. Читаемость скорее — это скорость усвоения информации. Если код очень емкий, то даже медленное чтение символов может дать значительный прирост производительности.

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

Есть еще языки, которые не работают с именами параметров. Например, самый известный — FP.

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

Очень логично, да. „

Конечно, логично. Когда ставится имя вместо слова var, то оно не создается. Мы указываем уже созданное имя. Вот после var — создается имя — имя экземпляра.
«Ну, вы же понимаете, что тело метода — две группировки, и они тоже не менее читаемы, чем SQL? Привести конкретный код?»

Конечно. Если уже сравнивать объем кода и читаемость, то надо предоставлять весь код. Иначе я ведь в SQL тоже мог написать табличну функцию GetUserActivity. И код был бы тривиальный без ее тела.

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

«Ууу. Нет, здесь я с вами спорить не буду, читайте МакКоннела, у него это явно описано неоднократно. И все аргументы у него тоже приведены.»

Скажу еще раз, читал. И что? Его мнение выше законов теории информации? Поясняю. Любое имя — это новая сущность. В смысле бритвы Оккама. Это «плюс» новая информация. Не «минус». Создание новых имен — это всегда выбор из двух зол. Каждое новое имя само по себе увеличивает сложность кода (есть такая мера — цикломатическая сложность". У майкрософт войдет ваш лишний метод). Объем информации в любом случае растет. Что же тогда улучшается? Может (но не факт!!) улучшиться восприятие отдельного куска кода. Потом, нужно смотреть спрятанный код. Чтобы уменьшить необходимость «подглядываний» в спрятанный код, хорошо придумывать имена, связанные с реальным миром, о которых у нас уже есть представление. Но само по себе придумывание имени волшебным образом не съедает информацию. Просто информация выходит за пределы кода (образно). Можно еще писать интерфейсы и описывать словами, что каждый метод делает. Что тоже — выходит информация за пределы кода.

Так что вот. Имена нужны, чтобы сузить внимание на небольшом числе объектов. Но другие объекты все таки не исчезли. И создание новых имен само по себе зло. Чтобы было меньше необходимости создавать имена, создают более мощные языки. Кстати, и лямбды поэтому и создали, а также Func<>. Чтобы не создавать новое имя — делегат. Чтобы можно было логику описать меньшим объемом текста.

А есть такие хитрые языки, которые даже лямбды обошли. Там не нужно не только имя методу, но даже и имена входным параметрам. Что делает язык значительно мощнее. Это APL и еще некоторые.
«Вы очередной раз невнимательны.»

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

«Код как раз аналогичен, потому что GetUserActivity — это аналог вашей CTE, только доменный. «Очевидно, что» где-то внутри себя он делает подготовку позиций, только мы это спрятали (получив, тем самым, уменьшение локальной сложности). „

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

мы по условиям не пользуемся функциональными фишками шарпа ))
«А стоит нам начать писать запрос в C# функционально (linq, кстат, не обязателен), как код станет более читаем, чем в SQL, в силу возможности введения доменных типов и операций.»

Не станет. Когда я вижу запрос, я вижу весь алгоритм, а не маленький кусочек. Код на шарпе будет значительно длиннее. Следовательно, весь код на шарпе будет тяжелее читаем. Этот можно напрячься и прочитать за пару минут. Без особого опыта за 10. Шарп, со множеством классов и алгоритмов, не будет читаться, уйдет больше времени. Плюс — возможность переполнения стека, вечные циклы — плата за императивное программирования
да и потом, не всегда легко читать SQL код. Мы тогда говорили о шарпе. Если шарп позволяет в том случае писать и читать линейно, то зачем эту возможность игнорировать?

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

Функциональные языки следуют парадигме, но не всегда идеальны для чтения. Вот на APL уже кидали ссылку. Но при определенной практике APL будет читаться значительно лучше и быстрее шарпа на одинаковых задачах, в силу того, что код значительно короче.
«actWithNums это что такое? Какая-то фигня, которая будет определена позже по порядку чтения?»

Линейно тоже читается. Читается так:
Пусть actWithNums (возможно не очень адекватное название, но SQL терпит, там большие названия затрудняют чтение) — это представление с колонками Position, UserId, ActivityDate, где Position — это позиция пользователя по количеству активностей за определенную дату ActivityDate. Тогда дай мне пять самых активных пользователей за сегодня, и их позиции за вчера.

Есть разница читать «Просим создать таблицу и наполнить ее группировкой. Потом выбираем из нее с данные и джойним с вчерашними данными». Очевидно, что «создаем» и «это» — разные вещи. А отношение «это» — соответствует работе сервера (мы не знаем последовательность действий, которую он будет выполнять).

На счет линейного восприятия. Аналогия с:
class Paymant
{

Читается — «класс Оплата — это ....». Т.е. здесь вводится новый термин вместе с определением
А попробуйте с конца читать:
«я хочу взять 5 активных пользователей за сегодня и их позиции за вчера, где позициями называем число активностей за день каждого пользователя»

По моему там так написано. А не «создаем СТЕ, выбираем наиболее активных пользователей».
Это и называется декларативным способом — это не решение, это описание, что я хочу. Джойны — всё равно что союзы в естественном языке. Я ни в коем случае не могу предполагать, в каком порядке что делает сервер. И вообще, делает ли он что-то. Может он вообще не джойнит и не ведет поиск, может быть закешировал до этого. Может были обращения другие к одной из таблиц и он содержит уже готовую статистику.

И не могу полагать, что он делает СТЕ, причем делает группировку для этого.
То, что вы читаете, как последовательность действий, это ваш способ представления о работе, но никак это не говорит, что SQL сервер в таком порядке выполняет действия.
Ок. Допустим у нас есть сущность «Активность пользователей», представленная таблицей:
create table [dbo].[UserActivity](
	[Id] [int] identity(1,1) not null,
	[UserId] [int] not null,
	[ActivityTime] [datetime] not null,
	primary key ([Id])
)


Вот запрос, решающий безнес-требование:
with actWithNums (Position, UserId, ActivityDate)
as
(
	select row_number() over (partition by uac.ActivityDate order by uac.dayActivityCount desc) as Position, uac.UserId, uac.ActivityDate from
	(
		select uafd.UserId, uafd.ActivityDate, count(*) as dayActivityCount from 
		(
			select Id, UserId, cast(ActivityTime as date) as ActivityDate from dbo.UserActivity
		) uafd
		group by uafd.UserId, uafd.ActivityDate
	) uac
)

select todayUsers.UserId, todayUsers.Position as TodayPosition, yesterdayUsers.Position as YesterdayPosition 
	from (select * from actWithNums where ActivityDate = cast(getdate() as date)) todayUsers
inner join (select * from actWithNums where ActivityDate = cast(dateadd(day, -1, getdate()) as date)) yesterdayUsers on yesterdayUsers.UserId = todayUsers.UserId
where todayUsers.Position <= 5


Код написан для SQL-SERVER. Не обращайте внимание на звездочки и что я не описываю таблицу пользователей и не джойнюсь к ней. Для примера пойдет.
«Хорошо, а в вопросе «реализуйте конкретную задачу декларативным образом на SQL»»

Ваши бизнес-требования? Можем в личной переписке. В форуме писать много кода или схемы, просто показывая, как моделируется предметная область в реляционной БД — думаю лишнее. Реляционная БД — вещь ограниченная, и не всё можно только связями смоделировать. Но очень многое можно. В том числе и процессы и принадлежности сущностей. А вообще, вопрос относился не к SQL и не к базам. На шарпе можно вообще всё моделировать. Базу я привел в пример, потому что она более строгая, что позволяет описать словами сам принцип моделирования. Так проще было отвечать.
«А вы понимаете, что отлавливать ошибку NullReferenceException, кинутую глубоко внутри стека, существенно дороже, чем ArgumentNullException, кинутую на периметре?»

Нет, не понимаю. У эексепшина есть стектрейс. И найти откуда взялся null, дело минут обычно. А вот строка:

if (arg == null)
{
throw new ArgumentNullException…
}

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

«Если он упал, то он не работает. Требование не выполнено.»
Конечно. А не упал, но работает неправильно — требование тоже не выполнено. Второй случай значительно опаснее, потому что баг продолжает жить и ворует деньги.

«Если этого не замечают, то это никому не важно. Тут все очень просто.»
)
Это не верно. В достаточно сложной системе может быть тяжело видеть утечку денег и неправильную работу.

Да, и кстати, это всё у Макконела есть. Не скажу в каких главах, давно читал, но определенно есть.

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

«Кстати, обратите внимание, как изящно вы вышли из беседы о функциональном программировании»

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

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

«Если коротко, то это невозможно.»

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

«А что плохого-то? Если у вас есть сервис в продуктиве, который должен работать без присмотра месяцами, то он должен обладать высокой живучестью.»

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

Высокая живучесть полезна только в несущественных вещах — например, гуи, где если лишняя черточка моргнет, никому особо не помещает. Или для взаимодействия с внешними системами — может быть, но под вопросом.
А, извините, ошибся в ссылке. Тогда Харрисон. Раньше и в википедии было определение «функциональной парадигмы», где давалось определение основного типа данных — списка, как head::tail.

Сейчас поправили, дали более общее определение. Я имею такое мнение, потому что знаком с программистами на функциональных языках. И там тоже бывают внутренние холивары, считать ли APL функциональным языком? Поэтому есть два мнения, что такое функциональный язык. Одни люди считают, что есть понятие «чистого функционального языка», которое в современном мире реализовано только на списках и внутри него обязательно лябмда-исчесление. Но при этом, считают, что такой идеал просто не все языки достигают.
Другие считают, что функциональный язык должен всего лишь выполнять некоторые требования: чистые функции и т.д.

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

Вот где-то так.

«Это почему, интересно? „

Потому что не связано с задачей. Захламляет код всяческими проверками и выбрасыванием эксепшинов. Дотнет — достаточно умная платформа и он сам бросает в случае чего довольно адекватные эксепшины. Есть сборщик мусора, нет проблем с памятью. В плюсах программа же может не падать, а работать и быть в неопределенном состоянии. Когда обратились к невыделенной памяти, когда работают с указателями напрямую.
Шарп же упадет в большинстве случаев. Принципы создания надежного ПО как раз в этом заключаются. Когда на шарпе пишут в стиле защитного программирования, то либо стараются сделать наоборот — код с высокой живучестью (очень плохо), либо просто дают другие имена эксепшинам, которые бросил бы код и так. Другие имена — это иногда полезно, но в общем, не думаю, что этим стоит вообще злоупотреблять — код захламляют лишние проверки. Другие имена стоит давать в случае, если это “бизнес-исключение» — т.е. не ошибка кода, а исключение, которое полезно для пользователя (разрыв коннекшина, например).

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

Information

Rating
Does not participate
Location
Киев, Киевская обл., Украина
Date of birth
Registered
Activity