Pull to refresh
34
0
Сергей Сотник @atepeq

Пользователь

Send message
Да, думаю, что машины, если будут писать «для себя» (т.е., без цели интегрировать код с человеческим), будут переводить его не в С, а во что-то другое. Возможно, какое-то графовое представление. А может — сразу в машинный код.

Тот подход, что описан здесь не очень хорошо поддается масштабированию влоб — в более-менее сложной системе (при произвольном языке описания) он приведет к проблемам, связанным с комбинаторной сложностью всех возможных вариантов. Скорее, этот подход может служить аналогом того, что у хорошего программиста получается сделать почти без раздумий — найти максимальный, минимальный элементы, отсортировать пузырьком, найти подстроку в массиве строк, загрузить объект (используя ORM) из DB. Дальнейшую комбинацию этих задач нужно осуществлять уже на другом уровне абстракции, где может стоять своя RNN (или что-то еще).

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

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

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

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

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


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

В свое время (еще в школе) я понял, что мне достаточно легко читать текст вверх ногами. Было занятно развить это качество до такой степени, что читал в таком положении примерно с такой скоростью, как и в правильной позиции. Дальше развлекался тем, что ирретировал окружающих тем, что демонстративно читал, держа книгу вверх ногами. Ребячество конечно. Лучше бы занялся чем-то другим, но в школе мы выбрасывали тонны времени и не на такое.

Этот пример я привел к тому, что все, с чем поработал некоторое время и освоил, будет красивым и привычным. Именно так, на большом количестве примеров и обучаются нейронные сети (и живые, и «железные»). Я не спорю с тем, что вам это ЧПС-выражение кажется прекрасным. Я только говорю, что это же можно решить иначе, будет не хуже (если вы будете некоторое время пользоваться этим) и не нужно будет (точнее, было) усложнять язык.

Мне интересно, а как по вашему люди пишут на нескольких языках сразу? Место в голове не заканчивается?:)


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

Разработка — это борьба со сложностью. Именно на это направлены различные приемы и приемчики, методы, Code Agreements, ограничения.
Да, конечно. Отвлекся на непрофильное использование using, а этот вариант не упомянул. Этот вариант тоже подойдет, но со своими ограничениями. Чтобы было более понятно — расширю пример

public class Keyword{...}
public class Entrances:List<...>{...}
public class KeywordsDictionary: Dictionary<Keyword, Entrances>{}

class DocumentStatistics {
	...
	KeywordsDictionary dictionary = new KeywordsDictionary();
	...
	void AugmentStatistics(KeywordsDictionary anotherDictionary){...}
	public Entrances GetEntrances(Keyword){...}
	...
}


Ну и этих вхождений типов Keyword и Entrances может быть некоторое количество по разным классам. Кстати, обратите внимание, что var тут никак не поможет. И вот, если конструкция одного из классов изменилась — скажем, Keyword стал чем-то более сложным, с новыми полями, то достаточно его объявление поменять один раз. Да, еще нужно будет покопаться в работе с реализацией, там где это было затронуто, но высокоуровневые объявления не затрагиваются. Это удобно.

К сожалению, есть ложка дегтя. Вы о ней сказали — sealed классы (а это тот же string). Еще добавлю различные базовые типы (int, float), которые часто поначалу используются в качестве ключей, элементов. В алгоритмах с высокой вычислительной нагрузкой все переводить на классы — это существенный overhead.

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

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

2. Никто не мешал реализовать конструктор парсера, который также относительно чисто будет работать во fluent-стиле (я сейчас набрасываю от балды, просто как идею):

public static Parser<Question> Question =
  ParcerBuilder
    .Clause(AnswerTypeIndicator.Or(Parse.Return(AnswerType.Text)))
    .Clause(Identifier)
    .Clause(QuotedText)
  .Build();


Для меня такой синтаксис также вполне понятен, а семантика соответствует тому, что ожидается от подобных конструкций. Чуть-чуть напрягает, что удобочитаемость таких конструкций зависит от размера и удачного форматирования кода — но это же относится и ЧПС LINQ и многому другому.

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

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

Насчет языков можете еще добавить программирование прямо в коде без ассемблера. Еще желательно на различных архитектурах. Мне именно этот опыт кажется крайне важным. Становится понятно, насколько нелогичной является архитектура x86 (по сравнению с другими). Это наслоение нескольких поколений эволюции, зачастую мешающих друг другу. Обязательно добавьте также использование NoSQL-систем. Я согласен, что перечисленные вами подходы (языки, технологии) нужно попробовать. И не на уровне прочтения спецификации языка. Это все было в разные периоды. В том числе и работа не с теми языками, которые мне нравились. Конечно, с них старался соскочить при первой возможности.

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

И вот именно здесь становится важна сложность инструмента. Чем меньше его сложность — тем легче его освоить. И тем больше инструментов можно опробовать и держать наготове.

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

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

Но никто не предложил своих вариантов (если не считать таковым призыва к самоограничению в комментарии https://habrahabr.ru/post/302076/#comment_9657336). Неужели есть только путь накопления фич и чем их больше, тем язык лучше?
С другой стороны есть динамические языки, где вообще типы переменных не указываются и люди как-то пишут. И даже хорошо пишут.


Да, есть такое. В спойлерах попытался объяснить, что я хорошо понимаю, что есть разные люди. Кому-то не мешает отсутствие опеки со стороны компилятора. Но я — не такой.

Обратите внимание, везде, где только возможно, я пытаюсь вставить оговорку, что это мой взгляд. Я уважительно отношусь к людям, пишущим на любых языках. Просто у меня они (динамические языки) не идут — бесят и требуют массу времени на выискивание опечаток и других дурацких ошибок. Когда-то я думал, что нужно просто больше заниматься программированием и читать правильную литературу. Но, со временем, понял, что многое зависит также от личных особенностей. У меня, например, ниже среднего объем кратковременной памяти (то самое магическое число 7±2). Есть и другие объективные отличия от среднего человека. Ничего необычного — просто я такой. Но эти отличия приводят к тому, что я начинаю сталкиваться с некоторыми проблемами раньше других (с другими же наоборот — позже). И мне приходится применять разные приемы и приемчики (например, делить на более короткие методы) там, где другие люди еще просто пишут код и не испытывают ни малейших проблем с охватом уровня полностью.

По совокупности всех этих причин, у меня не сложилось со скриптовыми языками. Там моя производительность низка. Это особенно печалит из-за того, что все самые перспективные библиотеки для Deep Learning имеют в качестве фронтэнда именно скриптовые языки, прежде всего Python (Lua, R). А к этому направлению я сейчас сильно присматриваюсь. Надеюсь, что в Гугле все же допилят нормальную поддержку Go в TensorFlow (на мой поверхностный взгляд, это все же самая перспективная open-source библиотека в данном направлении на сегодняшний момент).
Ну и если уж без коллизий или многократно используемых сложных имен типов совсем никуда то ограничения директивы using одним файлом скорее преимущество чем недостаток — чтобы эта зараза гарантированно автоматически не распространялась на полпроекта.


Согласен, именно для этого и подходит идеально конструкция using xxx=yyy. И с этой точки зрения ограничение в один исходный файл очень логично.
Хотел отметить, что мы немного отвлеклись от начальной темы — неудачного опыта использования using xxx=yyy, как замены typedef. Но с точки зрения решения конфликта имен она подходит хорошо.

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

Пример из недавнего.

Есть такая библиотека для работы с длинными именами файлов — AlphaFS. Удобная. Её модель — заменить классы из пространства System.IO на свою реализацию так, чтобы в большинстве случаев пришлось только заменить неймспейс System.IO на Alphaleonis.Win32.Filesystem. Но заменяется не все. Например, Alphaleonis.Win32.Filesystem.File.GetAttributes(...), возвращает System.IO.FileAttributes. Этот тип используется неоднократно по коду (не очень много, но достаточно, чтобы полное написание начало раздражать). Если добавим

using System.IO;


то пойдут конфликты между File из разных неймспейсов (хотя мы используем только один). Var не поможет, ибо там нечто вроде:

FileAttributes.ReadOnly | FileAttributes.System | FileAttributes.Hidden


Вот в этом случае

using FileAttributes=System.IO.FileAttributes;


подошло идеально.

Сценарий не частый, но using xxx=yyy уже не первый раз позволяет написать код более чистый, чем с помощью полной квалификации имен в коде.
Да, очень опасная (за счет потери плюсов от статической типизации) возможность. но она вроде бы не столь часто встречалась мне в коде.

Опять-таки, ограниченность личного опыта. Но после того, как (ткнули выше) увидел, что в Dapper использование dynamic это вполне себе обычный подход… Даже не знаю, что сказать.

Dynamic вводился для поддержки взаимодействия со скриптовыми языками. При правильном использовании (т.е., отсутсвие его использования в чисто C#-коде) он не должен доставлять особых хлопот. Но похоже, что все, что если что-то можно использовать не так как задумано и это сократит хоть на немного время написания (именно написания, а не отладки и поддержки) программы, будет использовано именно так.
Это больше похоже на религию — «Не обсуждать, что решили патриархи. Они умнее и все продумали за нас».

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

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

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

Насчет C++ вроде претензий не было.
Да, написано хорошо. И все же позволю себе добавить.

Эрик Липперт — один из идеологов C# (был ведущим разработчиком компилятора языка). Поэтому стоит рассматривать его аргументы через призму того, что он вполне может даже не замечая этого, подбирать аргументы под уже реализованную конструкцию языка.

Сравнивает он не var и короткое название типа (если бы была конструкция type/typedef), а var и монструозные Dictionary<string, List>. В этих случаях я бы тоже выбрал, пожалуй, var. Но я ведь не могу этот же var использовать в объявлении элементов, видимых извне методов, классов. Там, несмотря на то, что мне важна семантика, я вынужден бесконечно использовать именно это слоноподобное Dictionary<string, List>. А еще — переписывать все эти куски кода, как только решу использовать в качестве элемента словаря что-то другое, а не список децималов. Дайте описать короткий синоним типу и все эти проблемы решатся. И мы опять вернемся к семантике. К тому же, будет хорошо видно, куда посмотреть, если будет нужна структура типа, а не выводить её как компилятор, временами возвращаясь к предыдущим строкам. Таким образом, наберусь наглости сказать, что Липперт на самом деле дает аргументы в пользу type/typedef, просто у него есть только var.

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

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

Взять вот к примеру WPF, так как-то для Dependency Property обходятся без рефлексии и dynamic.

Да, конечно все можно написать и без динамиков. Но насчет без рефлексии… Я не сильно работаю с визуальной частью, так что не могу считать, что знаю WPF. Там разве внутри под капотом не сидит рефлексия?

лишь бы никто не стал переть это в открытые API

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

Вложенные дженерики это за редким исключением быдлокод.

Список словарей или очередь списков — вполне нормальный и обозримый случай. А это уже вложенные дженерики. Однако, даже просто дженерики (невложенные — например, Dictionary<string, int>) приходится повторять в нескольких местах. Даже с использованием var это приходится делать в каждом инициализаторе, в объявлении поля или свойства:

class SomeClass {
	...
	Dictionary<string, int> dictionary = new Dictionary<string, int>();
	...
	public void Insect(Dictionary<string, int> anotherDictionary){
	...
	}
}


И тут int решили поменять на uint (или сделать из него класс). Или сделать классом — идет мощное редактирование кода во всех этих местах. Если же сконцентрировать это описание в type, то изменить определение нужно будет только в одном месте. var служит той же цели, но в ограниченном количестве случаев. Кроме того, её легче использовать неправильно (заставляя человека выводить тип), чем имя типа.

Если у вас конфликтуют пространства имен — это быдлокод

Речь идет, прежде всего, об импортированных библиотеках (не моих). Типичный пример — импортирую, скажем 2 библиотеки, у которых есть класс Document (а он есть у многих библиотек). И они начинают конфликтовать. Что делать? У меня 2 пути:
1. Записывать квалификатор имени вместе с полным неймспейсом — несколько громоздко, если класс используется часто. В случае, если он по коду упоминается 1-2 раза, так и делаю.
2. Сделать синоним классу через using TheDocument = xxx.xxx.Document;
Я же пояснил, что using у меня остался, но для крайне ограниченного количества случаев, не связанных с использованием в качестве суррогата для type.
Да, да! Во вьюверах кода систем контроля версий решарпер не встраивается. А это очень распространенный кейс.
Ну, тут скорее нужно правильно именовать типы сразу. А вот с using беда в том, что действует это определение только в пределах того файла с исходником, в котором оно введено. Так что после некоторых экспериментов с ним забросил эту конструкцию. Она осталась только для решения вопроса при конфликте имен в разных библиотеках (чтобы не писать полный длинный путь к классу).
Свое мнение о том, что ЧПС не нужен в C#, я составил, исходя из следующих фактов:

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

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

Information

Rating
Does not participate
Location
Днепр, Днепропетровская обл., Украина
Registered
Activity