nsf mindstream

суббота, 28 февраля 2009 г.

Энергетика явлений

Знаете, я заметил такую штуку - чтобы (простите) высрать какое-нибудь творение на свет, а в моём случае программу, нужно N-ое количествое энергии. Той, внутренней, необъяснимой (или объяснимой, но рано ещё). Так вот, я считал до недавнего времени, что энергии у меня этой не хватает, а оказывается, что её таки избыток, но пользоваться ею я не умею %). Однако...

среда, 4 февраля 2009 г.

Методология разработки программ.

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

1. Пишем быстро (сроки ведь поджимают, не так ли?), почти не думаем, что пишем, т.е. пишем на автомате, потом запускаем в надежде, что заработает. Если заработало, верим, что работает во всех случаях и пишем дальше. Когда всплывают баги, фиксим.

В итоге получается куча костылей, но код абы как работает, баги всплывают часто, но и исправляются быстро. Продукт получается тоже быстро.

2. Пишем медленно, после того как придумали архитектуру, каждая функция вылизывается, проверяются значения входные, чётко описываем все возможные варианты исхода. Когда используем чужую функцию, вообще не забиваем на проверку ошибок, исключений, буквально обрабатываем каждый вариант и следим, чтобы всё имело смысл. После написания модуля/части программы, пишем также старательно кучу тестов, перепроверяем 10 раз. Баги должны всплывать редко, но страдает время изготовления продукта. За счет того, что архитектура должна быть адекватной, случаются не редко большие рефакторинги, однако вероятно в больших проектах это позволяет как-то где-то удержать разрастающуюся сложность под контролем.

Так проходит 5 лет и продукт релизится, хороший стабильный, времени затрачено уйма, надеемся, что железо и требования не переросли продукт. PROFIT!

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

воскресенье, 25 января 2009 г.

Софт - говно

Ага, вот такое вот название, не совсем приличное. А что уж там, совсем неприличное. Я часто люблю критиковать чужие работы в негативной форме. Говорю, что фильмы говно, софт говно, игры говно, да что угодно говно. Однако недавно встретил фразу, которая заставила задуматься о том, какую я ответственность беру на себя говоря такие слова и оказалось, что я просто пустослов. Постараюсь реже говорить такие вещи.

Фраза была такая:

"Не согласен - критикуй, критикуешь - предлагай, предлагаешь - делай, делаешь - отвечай." Королёв, Сергей Павлович

Эти слова многое говорят об ответственности и карме. Также как действия не бывает без намерения и в данном случае одного не бывает без другого. А если же кажется, что это не так, значит под "чем-то" скрывается то, что этим чем-то не является. Ай-яй-яй.

пятница, 9 января 2009 г.

Неопределенность

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

В 2008 году написал bmpanel. Чем собственно доволен, но недоволен тем, что написал только её. Был ещё termbox, но польза сомнительна. Другая вещь которую наверно стоит отметить - первый полный год который я провел за ОС linux. Тут мне и по сей день нравится, возвращаться в wiNd0w$ не собираюсь. Писать почему не буду, т.к. я считаю это личное дело каждого, что использовать/предпочитать и что ненавидеть. Бросил институт, доучился до 4 курса, надоело. Хорошо это или плохо пока не знаю, увидим. Было ещё некоторое количество значимых событий, но о них я предпочитаю не говорить публично, поэтому на этом и закончим некое подведение итогов.

Определенно хотелось бы большего от себя, но не получается. Почему? Не знаю. Ощущаю неопределенность. Стукнуло 20, а я всё ещё иду в никуда, не знаю чего хочу. Чё-то делаю там сям, чему-то учусь, что-то решаю, но не придаю значение ничему. Проблема выбора направления судьбы с одной стороны, но имеем ли мы этот выбор вообще? Опять не знаю. Неопределенность :) Вот так и существую, как корабль на волнах чужого моря. Куда забросит, тому и благодарен. Но как ни странно в жизни мне везет если это можно так назвать. Часто складываются благоприятные обстоятельства во множестве разных течений. Почему так? Опять не знаю, может просто карма хорошая.

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

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

четверг, 4 декабря 2008 г.

Twitter

Зарегался на twitter.com. Можете там отслеживать, чем я страдаю обычно вместо кодинга %)
http://twitter.com/nosmileface

ООП в языках

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

Итак попорядку. Мне не нравится термин <эдакое> программирование, потому что я понимаю под программированием процесс превращения дизайна программы в непосредственно код. То есть существует некоторый дизайн-документ, где подробно описан каждый аспект программы и чтобы дизайн-документ стал непосредственно программой, нужно написать реализацию согласно этому дизайн-документу на конкретном языке программирования. Помоему часто программированием называют как процесс создания дизайна программы, её архитектуры, так и процесс непосредственно материализации этого дизайна в виде кода на конкретном языке программирования. Вероятней всего это случилось от того, что в древние времена роль дизайнера и программиста исполнял один человек или даже группа, но всё равно не стояла задача разделения таких понятий. Вообщем не столь важны причины, просто отмечу, что я не люблю называть программированием весь процесс создания программы. Я разделаю весь процесс на две части: дизайн и программирование.

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

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

Но конечно же эти два этапа последовательны только в идеале. Зачастую они прогрессируют параллельно. Части дизайна переписываются и изменения отражаются в коде программы. Процесс приобретает итеративный вид. Иными словами так чаще всего (если не всегда) случается на практике. Поэтому вероятно и обзывают всё это одним словом - программирование.

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

Что я имею ввиду? Давайте посмотрим на примере. Пусть в терминах объектно-ориентированного дизайна существует некая абстракция - интерфейс Reader. А также существуют его реализации: ZipReader, MemoryReader и FileReader.

Давайте напишем реализацию на языке Си:


struct Reader;
struct ReaderInterface { /* it's like vtable */
void (*close)(struct Reader *reader);
int (*read)(struct Reader *reader, void *memory, size_t size);
};

struct Reader {
struct ReaderInterface *interface;
void *private_data;
};

/* inline functions */
inline void rclose(struct Reader *reader)
{
reader->interface->close(reader);
}
inline int rread(struct Reader *reader, void *memory, size_t size)
{
return (*reader->interface->read)(reader, memory, size);
}

/* now we're defining somewhere interfaces with their private_data
meaning and with their own interface table */

struct Reader *create_zip_reader(const char *zipfile, const char *file_in_zip);
struct Reader *create_file_reader(const char *filename);
struct Reader *create_memory_reader(const void *ptr, size_t size);

/* and the usage */
void usage_function()
{
struct Reader *r = create_zip_reader("archive.zip", "file/in/archive.txt");
if (!r) {
error();
return;
}

int a = 0, b = 0, c = 0;

rread(r, &a, sizeof(int));
rread(r, &b, sizeof(int));
rread(r, &c, sizeof(int));

rclose(r);
}

А теперь напишем то же самое на С++:


struct Reader {
virtual ~Reader() {} // good C++ style, virtual destructor in interface
// virtual void close() = 0;
// no close() function, closing on delete
virtual int read(void *memory, size_t size) = 0;
};

Reader *create_zip_reader(const char *zipfile, const char *file_in_zip);
Reader *create_file_reader(const char *filename);
Reader *create_memory_reader(const void *ptr, size_t size);

void usage_function()
{
Reader *r = create_zip_reader("archive.zip", "file/in/archive.txt");
if (!r) {
error();
return;
}

int a = 0, b = 0, c = 0;

r->read(&a, sizeof(int));
r->read(&b, sizeof(int));
r->read(&c, sizeof(int));

delete r;
}

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

Каждая конкретная реализация методики полюбому имеет свои ограничения. А что если я захочу так, как делать нельзя? Приходится думать как обойти проблему в рамках поддержки этих конструкций в языке и если не получается, то мы спускаемся на уровень языка Си, где вообще никаких средств для реализации тех или иных идей из ООД нет. Или другой случай, когда появляется новая методика и её невозможно выразить в терминах старой методики, не переписывать же язык! Поэтому я считаю, что каждая конкретная реализация методики не нужна, ибо имеет слишком узкую область применения. А что если попробовать реализовать поддержку методик ООД, без конкретики. Допустим предложить некий синтаксис для непосредственных манипуляций с vtable или ещё что-нибудь достаточно общее, но не вводить понятия класс, объект и наследование. Впрочем надо сказать, что и самого языка Си уже достаточно, т.к. на примере выше можно чётко видеть, что простые inline функции почти решают проблемы нечитаемого вида обращений к виртуальной таблице. 

Впрочем, возможно я погорячился и поддержка ООД действительно не может иметь никаких иных удобных для использования форм. Да и в конце концов все средства ООД в основном в С++ это операции с virtual table путем формирования иерархий классов. Возможно это и не так критично для языка. Но что однозначно я бы покритиковал, это языки в которых "всё является объектом". Это неправильный подход к формированию средств кодогенерации однозначно.

Но если же отойти от примера с ООД и вспомнить про другие техники, начать мыслить обобщенно и пойти по этому направлению. В голове начинают всплывать всяческие страшные картины языка в котором можно модифицировать синтаксис и придумывать новые конструкции и/или модифицировать уже существующие меняя их семантику. И рядом тут ещё метапрограммирование где-то стоит. Но стоит взглянуть на современные языки типа Nemerle или D, то оказывается, что мысли не так далеки от правды. В этих языках уже можно определять новые синтаксические конструкции и представлять свою семантику в виде макросов, которые оперируют с AST (abstract syntax tree). Поживем - увидим. Может именно это направление и будет ключевым в ближайшие 10-20 лет развития средств кодогенерации. Язык с динамическим синтаксисом и плавающей семантикой. Как вам такое?

понедельник, 1 декабря 2008 г.

Шаблоны и библиотеки

В продолжении темы про кодогенерацию опять нахлынуло на меня несколько мыслей по этому поводу. Многие говорят: "Шаблоны, шаблоны! Generic programming! Создание компонент для настоящего повторного использования". Но как всё обстоит на самом деле? Любой шаблонный класс или шаблонная библиотека скорее всего представляют собой инструмент кодогенерации, а сам механизм шаблонов средство генерации средств кодогенерации :) Т.е. да, у шаблонов и препроцессора много общего. Использовать библиотеки на шаблонах зачастую значит копипастить и плодить код, пусть и потенциально более оптимальный. Конечно важно иметь такую возможность, особенно работая с кодом у которого высокие требования к производительности, но ведь шаблоны сплошь и рядом. То, что можно было смело засунуть в shared object, пихается в header'ы теперь. Понимаю на системах, где даже libc принято линковать статически (да да, я о Windows), там такие штуки никого не удивляют. Но там же и не удивляет никого очередной продукт софтостроения на пару гигабайт весом. А я считаю компоненты предназначенные для повторного использования и должны иметь вид отдельных shared object'ов. По крайней мере надо понимать и однозначно разграничивать, что является Библиотекой с большой буквы, а что является инструментом кодогенерации. А ещё более важно знать на столько ли нужен супер оптимальный код, подстроеный под конкретный тип или может быть по настоящему generic версия в библиотеке сгодится вполне. И ведь вроде так просто называть вещи своими именами. Не все почему-то это делают.