2008-12-25

Технология надежной и удобной аутентификация для web

Написано для: habrahabr.ru
Время написания: январь 2008


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

В чем именно неэффективность каждого из существующих способов?
  • Простой пароль: удобно, но есть несколько угроз, и самая главная даже не столько несанкционированное ознакомление с ним, сколько то, что примерно одна и та же комбинация логин/пароль может быть использована для множества разнообразных сервисов, часть из которых может быть недостаточно защищена.
  • Одноразовые пароли: безопасно и относительно удобно (но, все-таки, добавляется лишнее устройство), но довольно дорого.
  • Сертификаты цифровой подписи: безопасно, но очень неудобно (проблемы с кросс-платформенной поддержкой токенов), а также дорого.
  • Использование второго канала связи для подтверждения (обычно, мобильного телефона): относительно безопасно, относительно удобно, относительно масштабируемо (пока...).
  • OpenID: безопасно, но на данный момент труднодоступно из-за того, что у 99% людей нет доверенного веб-сервера.


Однако, сейчас уже можно замахнуться на глобальную систему аутентификации, если использовать сочетание ставших уже реальностью 3 феноменов:
  • IPv6;
  • OpenID;
  • стабильное интернет подключение с мобильного телефона/коммуникатора.

Вот она:
Каждый мобильный телефон, находясь в сети провайдера, будет постоянно подключен к интернету и иметь статический IPv6 адрес, а также DNS вида <номер телефона>.<домен оператора>. В каждом телефоне будет встроен сервис OpenID.
Таким образом, человеку нужно будет лишь каждое утро логиниться на свой телефон для того, чтобы иметь возможность автоматически аутентифицироваться на любом сайте. В такой системе, разумеется, появляется уязвимое место — сам телефон, в случае завладения которым, злоумышленники могут выдать себя за его владельца. Но тут, даже на первый взгляд, видится достаточно много способов защиты:

  1. (не говоря о блокировке телефона по звонку оператору);
  2. для каких-то чувствительных тракзакций (например, платежей) можно сделать дополнительную авторизацию в виде, например, пароля (вот уже и двухфакторная аутентификация);
  3. можно добавить биометрическую аутентификацию или использование дополнительного токена, например, RFID-брелка, который человек может носить на связке ключей, шее или запястье, и который должен находится не дальше, например, 2 м от телефона, чтобы работал OpenID сервис.
    Думаю, есть и другие разумные способы...



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

2008-12-23

Surprise with Lambdas

Tobias Rittweiler posted an explanation about the difference between (lambda and #'(lambda. The article is really useful, because I remember several times seeing the question in discussions unanswered.
Well, after reading LoL I sticked to using (lambda variant and I consider it to be the right thing: the less tokens, the less questions and confusion.

All I can add is, that Common Lisp continues to surprise me with how well thought it's design is. So, throw away the reader macros — it works by itself :)

2008-12-19

Мой текущий проект: fin-ack.com

Написано для: habrahabr.ru
Время написания: декабрь 2008

1. Кому нужен учет личных финансов?


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

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

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

Разумеется, многие осознали для себя необходимость вести организованный учет (а кого-то это заставляют делать те или иные жизненные обстоятельства) и уже используют какие-то средства. Я, например, начинал с обычных текстовых файлов. Большинство, конечно, предпочтут Excel из-за встроенных в него финансовых функций. Кто-то идет дальше и использует для этого специализированные программы: GNU Cash, MS Money, Quicken или еще одну из сотни аналогов. Многие из этих программ написаны для PDA и дают огромное преимущество того, что находятся всегда под рукой, однако теряют в функциональности...

Этот подход, безусловно, самый простой. Самый ли удобный? Все зависит от ситуации.

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

  • Другой, пожалуй даже большей головной болью является забота о резервном копировании.

  • Безопасность, на первый взгляд, кажется в лучшем состоянии по сравнению с хранением своих данных у 3-х лиц, однако, PDA можно потерять или в него могут приспокойно заглянуть коллеги по работе или знакомые (пока вы куда-то отлучились), а в файлы на стационарном компьютере тоже может залезть кто-то. Самое неприятное тут то, что это может сделать кто-то из тех, кто вас знает, а, по сути, только такие люди могут быть заинтересованы в том, чтобы узнать о ваших финансах.

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

Я думаю, понятно, что мы предлагаем подход к учету финансов в облаке, на веб. Он лишен описанных выше недостатков, однако, безусловно, имеет определенные особенности. Разумеется, идея не оригинальна, в том числе и в сфере личных финансов. Когда я заинтересовался этим вопросом года 2 назад, уже начали появляться подобные системы. На западе их уже, наверное, около десятка. Зачем нужна еще одна?


2. Наш подход и принципы, на которых построен сервис


Повторюсь, у каждого, наверно, есть примеры того, как веб-средства хранения личной информации выводят работу с ней на новый уровень. Забавно видеть, когда коллеги из разных компаний, которые пользуются корпоративной почтой, теряют время и бизнес-возможности из-за того, что оказываясь вне офиса (в коммандировке или на больничном) не могут получить доступ к ней. В итоге, все равно, приходится использовать G- или Y-mail. А сколько денег приходится тратить компаниям для обеспечения доступа к корпоративной почте через веб?.. Для меня такой killer app в свое время стал del.icio.us, который вернул мне возможность полноценно и легко пользоваться букмарками. Кстати, именно del.icio.us (в его предыдущей инкарнации) был вдохновением в дизайне пользовательского интерфейса для fin-ack.

В сфере управления небольшими объемами данных веб-сервис — это, как говорят, американцы win-win. И разработка с поддержкой проще и дешевле, и использование. Веб-сервис выигрывает за счет эффекта масштаба. Если каждому из тысячи пользователей нужно потратить на организацию бэкапа своих данных 10 минут, то даже если проработка сложной и надежной схемы резервного копирования займет у разработчиков день, эти 8 часов все равно не сравнить с 160 часами, потраченными каждым пользователем в отдельности. Также и на поддержку.

Чем же уникально наше решение? Конечно, я изучал то, что уже есть, а также (по возможности) мнения людей. Говорят, главная проблема с учетом финансов в том, что людям лень постоянно вводить свои траты и доходы. На этом предположении даже основана система 4konverta. В Америке эту проблему решают по своему: поскольку у них 90% рассчетов происходит по карточкам (т.е. в безналичной форме), они берут информацию о тратах прямо у банков, и каждая из систем пытается конкурировать в том, кто лучше автоматически обработает эту информацию, категоризирует ее и т.д. (По-моему, это выброс усилий на ветер). Да и удивляешься доверчивости американцев, спокойно отдающим аутентификационные данные от своего счета третьей стороне. Скольку уже было скандалов с кражей личных данных у любых компаний, начиная с AOL?! Другим недостатком такого подхода является то, что их системы заточены под автоматический учет трат, поэтому у них зачастую даже нет возможности вносить их вручную (например, насколько я помню, так устроена система Mint).

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

  • Во-первых, нужно самому придумать удобный для себя workflow. Например, мне совершенно не интересно, что в моем чеке из супермаркета 20 наименований продуктов. Я запишу их просто как "еда" и поставлю общую сумму. В среднем за день мне нужно ввести в систему 3-5 записей, а занимает это 3-5 минут. За это время я могу посмотреть на свои балансы, подумать о планах, сделать еще какой-то анализ.

  • Интерфейс системы ввода должен быть удобным (это, вроде как, понятно, но, почему-то, не всегда делается)

  • Не нужно запоминать свои траты — их нужно вводить сразу. Для этого мы разработали специальный мобильный клиент. У которого к тому же есть некоторые другие функции, актуальные для устройства в вашем кармане.

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

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

  • Передача данных защищена SSL. Данные хранятся на нашем сервере, а не у третьих сторон.

  • Ну и, в конце концов, опять же, все зависит от пользователя. Если есть опасения на предмет чистоты той или иной транзакции, или не стоит вносить ее в систему, или можно внести в какой-то непонятной для других форме.

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

Мы выбрали хорошо зарекомендовавшую себя в этой сфере (например, проектами 37signals) модель частично платной системы. Это значит, что те функции, которые являются действительно отличительными, будут иметь небольшую абонплату. В то же время базовой учетной системой всегда можно будет пользоваться бесплатно. Более того, счета будут выставляться по истечении 3-х месяцев постфактум, т.е. будет возможность полноценно попробовать систему в течение этого срока, а затем решить, использовать ли ее дальше и в какой форме: платной или бесплатной. При этом мы никому не будем передавать или продавать данные пользователей!

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

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

  • инструменты для решения своих задач;

  • рассказать, как ими можно пользоваться;

  • показать примеры разных подходов к их применению.

Fin-ack.com не является Web2.0 приложением, но он к этому движется. По моему мнению, в большинстве случаев создания веб-сервисов именно так и нужно поступать: сначала должна быть добротная базовая система, которая не хуже оффлайн аналогов. А по мере привлечения пользователей она должна развиваться вместе с ними и создавать возможности, просто недоступные для своих оффлайн конкурентов. Это и есть реализация одного из принципов Web2.0 по Тиму О'Рейлли: системы, которая становится тем лучше, чем больше людей ее используют. У нас есть идеи, как можно использовать аггрегированную информацию множества пользователей в их интересах, но об этом не стоит говорить до их появления. Я уверен, что многие идеи прийдут от пользователей.

В то же время мы движемся и дальше — к Web3.0 :) Если так можно назвать тенденцию, которую тот же Тим О'Рейлли называет "transcend the web", а другие — тем, что большинство людей в будущем будут работать с веб-приложениями посредством своего мобильного телефона. А в мобильный нельзя "запихнуть" веб-сайт. Приложения для него должны разрабатываться с учетом ограничений этого устройства, а также тех контекстных возможномтей которые оно дает.

Рассказывать о конкретных особенностях нашей системы не буду, статья и так уже достаточно большая. Надеюсь, fin-ack.com сам способен рассказать о себе.

PS.


Почему в теме Lisp? Ну, потому что вся серверная часть написана на Lisp... :)

2008-12-15

Парадигмы программирования

Со всем спектром придуманных на сегодня парадигм программирования можно ознакомиться в Википедии. В этой статье я хотел остановиться на тех из них, которые имеют значительное влияние на современные языки и программные среды, и о которых, соответственно, обязательно иметь представление любому разработчику. Обязательно потому, что за каждой из парадигм стоит огромная работа по поиску путей решения типичных проблем, возникающих при программировании "интуитивным"[1] путем, и не учитывать этот опыт — значит наступать на те же грабли в который раз и заново придумывать велосипед. Разумеется, многое из написанного в статье общеизвестно, но несмотря на это дискуссии на эти (базовые) темы возникают вновь и вновь. Эта статья — попытка упорядочить для себя общую картину, а также установить точку отсчета, к которой можно было бы привязываться впоследствии.

Основные парадигмы


  • Структурное программирование
    Эта парадигма представляет в основном теоретическое значение (хотя и является неотъемлемой частью практически всех современных языков). По сути, это был первый сдвиг парадигмы в программировании, когда после появления первых абстрактных и высокоуровневых по сравнению с ассемблером языков, пришло осознание, что разрабатывать на них также нужно на более высоком уровне абстракции, чем на ассемблере: в терминах условных выражений и циклов, а не передачи управления между метками. И, что не удивительно, был достигнут консенсус, неоднократно с тех пор подтвержденный практикой, что при использовании более абстрактных конструкций программирование становится более доступным, а программы — расширяемыми, поддерживаемыми,— и, в целом, можно решать намного более сложные задачи.
    Эта парадигма основывается на работах Дейкстры, Хоара, Вирта и других, которые доказали, что любое вычисление можно выразить через 3 базовые операции:

    • последовательное выполнение;
    • условный переход;
    • цикл с условием.

    Девиз структурного программирования — "GOTO является вредным".


  • Объектно-ориентированное программирование
    Это самая распространенная на сегодняшний день парадигма, которая является развитием идей структурного программирования. Она подается как реализация "естественного" взгляда на окружающий мир, в котором всё является объектом. Она, как известно, покоится на 3-х китах:

    • инкапсуляция;
    • наследование;
    • полиморфизм.

    Однако, учитывая распространение, которое она приобрела, а также, наверное, тот факт, что понятие "естественного" и строго математического определения немного отличаются, каждый ОО-язык понимает эти 3 концепции по-своему (во всяком случае, последние две), и каждое такое понимание имеет право на жизнь и применение.
    Наследование включает, условно говоря, наследование "свойств" и "функциональности".
    Для свойств оно может быть основано на классе (от абстрактного к конкретному) — см. C++, Java,— или же на прототипе (от конкретного к абстрактному) — JavaScript.
    Наследование же функциональности и полиморфизм — это 2 стороны одной медали. В Lisp подходе, основанном на родовых функциях эти 2 концепции унифицируются в рамках одной абстракции. Наследование функциональности может пониматься по-разному: как наследование реализации или как наследование интерфейса (см. http://weblog.raganwald.com/2008/04/is-strictly-equivalent-to.html). С другой стороны спектра находится обобщение подхода родовых функций — мультиметоды Clojure — диспетчиризация не только по типу аргументов, а и по любому предикату от аргументов.

    Самой распространненой, однако не единственной рализацией ОО-модели является придуманная в SmallTalk и перенятая в той или иной степени C++, Java, Python и другими основными современнымя языками модель передачи сообщений (диспетчиризация метода по типу первого аргумента, который является объектом, принимающим сообщение). Этот подход я бы еще назвал субъектно-ориентированным программированием, потому что в нем неявно считается, что каждый объект совершает действия, т.е. становится субъектом. В зависимости от динамичности языка в нем может поддерживаться утиная типизация (Duck typing), согласно которой для вызова метода для объекта этот объект должен иметь метод с такой сигнатурой, при этом его тип может не проверяться (динамическая передача сообщений).


  • Функциональное программирование
    Эта парадигма имеет свои корни в Лямбда-исчислении Черча и ее первой реализации — оригинальному Lisp'у — больше лет, чем первому структурному языку Algol-60.
    Сейчас функциональная парадигма (в форме декларативного программирования) противопоставляется императивному подходу. Ее основа — это функция в математическом понимании (преобразование входных данных), а не функция как процедура, меняющая состяние мира.
    Концепциями функциональной парадигмы являются:

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

    Основным направлением в ФП сейчас направлением являются т.н. "чисто" функциональные языки, которые реализуют такие концепции, как:

    • ленивые вычисления
    • строгая типизация и вывод типов
    • сопоставление с образцом (pattern matching)

    Это Haskell, ML (Ocaml, SML), Scala и Qi.
    Однако, и динамические (не поддерживающие строгую типизацию) функциональные языки (как правило, имеющие Lisp-основу) также существуют и активно развиваются: Scheme, Erlang, Clojure.


  • Мета-программирование
    Основными идеями этой парадигмы являются возможности расширения базового языка превоклассными (такими, которые смогут использоваться наравне со встроенными) конструкциями, а также всегда программирование на уровне адекватном логике решаемой задачи и прдеметной области. Мета-программирование поддерживается методологией проектирования и разработки снизу-вверх, при которой сложная система разбивается на уровни, каждый из которых соответствует определенным независимым горизонтельным задачам: от уровня утилит-расширений языка вплоть до уровня доменно-специфического языка для моделирования той или иной прикладной области,— и реализуется постепенно уровень за уровнем. При этом на каждом уровне сложность задачи не увеличивается, поскольку примитивами на нем являются абстракции, выработанные на более низких уровнях.
    Таким образом мета-рограммирование порождает концепцию DSL — доменно специфических языков, создаваемых для той или иной предметной области (языко-ориентированное программирование), одновременно и использующими возможности хост-языка (все его базовые подсистемы, такие как: сбор мусора, математические библиотеки и т.д.), и являющимися адаптированными в синтаксисе и семантике к той сфере, для которой они реализованы.
    К мета-программным возможностям можно отнести те или иные особенности многих языков. Например, в C это перпроцессор, в C++ — в каком-то смысле, перегрузка операторов, а также применение шаблонов для абстракции на уровне системы типов. В Python — это механизм декораторов, позволяющий динамически дополнять реализацию методов. Попытки добавить определенные макросистемы делаются во многих языках. Это, например, MetaLua, а также модули andand, rewrite, rubymacros и др. в Ruby и т.д. Безусловно, базовым языком для парадигмы мета-программирования является Lisp, в котором и зародились (и продолжают зарождатся) ее концепции.


  • Скриптинговые языки
    Можно сказать, что это недопарадигма, поскольку она не основывается на какие-то фундаментальные исследования или принципы. Основная идея тут — максимальное удобство и простота ad-hoc реализации опеределенного класса решений на определенной архитектуре (иными словами, практичность). Это обуславливает такие характеристики языка, как:

    • динамичность
    • интерпретируемость
    • привязка к хост-системе

    В рамках этой парадигмы получили свое рождение такие языки, как: Perl (и PHP), Python, Ruby, Tcl, Lua, Basic, JavaScipt, Shell-языки (Bash, PowerShell, ActionScript, AppleScript, ...), некоторые из которых впоследствии переросли ее и перешли, как правило, в категорию объектно-ориентированных языков.


  • Программирование, ориентированное на параллелизм
    Это самая новая парадигма и, пожалуй, еще не до конца сформировавшаяся, поскольку пока что нет косенсуса на счет того, какая из предложенных концепций станет общепризнанной (если это вообще произойдет). А это и развитие архитектуры, подобной MapReduce Google, и асинхронная передача сообщений между процессами (полностью без общего состояния) Erlang'а, и использование программной транзакционной памяти — обобщения концепции транзакции в базах данных на любое вычисление (Haskell, Clojure). Основой для выделение этого подхода в отдельную парадигму стало понимание того, что использование блоков для синхронизации параллельных вычислений не является масштабируемым и поддерживаемым решением при реализации многопоточности (в том числе см. http://jcp.org/en/jsr/detail?id=166). Можно провести параллели между этой парадигмой и процедурной: проблема с использованием блоков аналогична проблеме goto. Пока что ясно одно: эта парадигма станет развитием функционального подхода с его краеугольными камнями немутируемых структур данных и ссылочной целостности.


Дуализм типизации


Несмотря на распространенное мнение поддержка языком программирования того или иного вида типизации является ортогональным к парадигме, которую этот язык реализует. Более того, вопрос типизации можно рассматривать в 2-х аспектах:

  1. Слабая vs сильная. Это различие скорее количественное, чем качественное. "Абсолютно" сильная типизация (как это реализовано в Haskell'е) не позволяет никакого приведения типов (во время исполнения). Более слабые системы типизации дают такую возможность до определенной степени. Например, многие источники называют С слабо-типизированным языком, поскольку он позволяет выполнять неявное приведение типов, а также явное приведение указателей (что не дает возможность проверить тип на этапе компиляции).
  2. Статическая (проверка типов во время компиляции) vs динамическая (проверка на этапе исполнения). Суть динамической типизации можно изложить во фразе из Common Lisp: "у переменных нет типов, типы есть только у значений". Это не значит, что типы не проверяются (как это происходит в нетипизированных языках, тких как Assembler и Forth, но они проверяются при выполнении операций над конкретными значениями во время исполнения программы. Поэтому в динамическом с сильной типизацией Lisp'е не может возникнуть ошибки сегментации памяти при попытке использовать значение не того типа, которая часто встречается в статическом со слабой типизацией С. По мнению идеологов статической типизации, благодаря ее использованию можно исправить большой класс ошибок в программе, связанных с использованием несоответствующих типов, с помощью явного указания типов и их проверки компилятором. А также увеличить быстродействие программы благодаря отсутствию необходимости проверки типов во время исполнения. В то же время написание таких программ является более длительным и сложным процессом (языки со строгой статической типизацией еще называют bondage & discipline languages), и что самое неприятное, их становится очень трудно менять впоследствии. Поэтому, наверное, ни тот ни другой подход не являются предпочтительными в общем случае и могут быть оба использованы в зависимости от конкретной задачи и преференций разработчиков. В идеале, должны появиться языки, которые будут позволять задействовать обе системы параллельно или на выбор. Например, в Common Lisp есть возможность объявлять типы для выражений и переменных для того, чтобы отключить их проверку во время исполнения — это решает проблему скорости для динамического языка, однако не адресует вопрос статической проверки типов при компиляции.

Ссылки на другие интересные парадигмы


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

  • Программирование на основе стека
    Языки на основе стека — это очень простые языки, примитивные операции которых построенны вокруг манипуляции этой структурой данных. Такой подход, в основном, избавляет от необходимости использования переменных для ссылок на значения (point-free programming). Кроме того, синтаксис таких языков становится весьма унифицированным (как правило, используется постфиксная нотация), что дает возможность задействовать мета-программирование.
    Языками на основе стека являются PostScript и Forth. Кроме того, большинство виртуальных машин современных языков, такие как JVM, .Net CLR, Perl Parrot и т.д. также являются языками основанными на стеке.

  • Логическое программирование
    Это реализация в языке модели формальной логики. При этом результаты работы программы часто являются побочными эффектами логического вывода. Интерес таких языков в том, что они предлагают совершенно иную модель вычислений, чем фон Неймановская архитектура или Лямбда-исчесление Черча, соответственно некоторые задачи, например, связанные с праллельными вычислениями или имеющие четкую математическую модель, можно решать с применением совсем других подходов. В рамках этой парадигмы получили развитие такие концепции, как бэктрекинг и сравнение с образцом (pattern matching).

  • Программирование в массивах
    Это направление было введено в APL и продолжает развиваться в его последователях J/K/Q, также его реализации присутствуют в MatLab'е и Mathematica. Базовая идея заключается в том, чтобы обобщить скалярные операции на векторные типы данных. Кроме того в этих языках, как правило поддерживается point-free programming. Их удобно использовать для выполнения сложных математических вычислений и они представляют скорее исследовательский интерес. Интересным примером промышленной системы с использованием таких языков является БД kdb+.


P.S



В заключение хотелось бы упомянуть еще одну парадигму или, скорее, анти-парадигму: эзотерические языки (эзоязыки). Эту концепцию также называют Turing tarpit (бочка дегтя Тюринга), потому что эзоязыки показывают, что можно легко создать Тюринг-полный язык, на котором будет совершенно невозможно программировать. По-моему, это хороший урок для тех, кто утверждает, что язык программирования не имеет значения. Да, на любом Тюринг-полном языке можно реализовать любой алгоритм и любую программу, но поробуйте сделать это на brainfuck'е, использующем только 8 допустимых символов и 8 операций, на котором "Hello world" выглядит так:
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
, или на Whitespace, для которого весь набор значащих символов составляют пробел, табуляция и новая строка.


[1] Как верно замечено в книге "Основы практики на пианино" в сложных областях деятельности существует настолько много методов решения задач, что даже не смотря на неплохие способности человеческого мозга навскидку выделять более эффектиные из них (которые мы называем "интуитивными"), даже среди этого подмножества есть группа методов, которые являются на порядки эффективнее других, но для того, чтобы их найти, простой интуиции не достаточно.

2008-12-14

1 namespace to rule them all

This thing scares and confuses me: filter even [1..10]
Is even a function or a var?
Personally I'd prefer: filter #'even [1..10]

Common Lisp's n-namespace property is one of the most underestimated features of this most underestimated language. In CL you virtually always know, what you are dealing with, a variable or a function. "Phew!" you say, "in C++ as well". Not so fast. If you try to use callbacks you don't. And when we enter the realm of the functional paradigm, where functions fly all around like daggers in the infamous house, it's really good to know for sure.

Let's restate the rules:
1. Prefix notation: in first position of a form should always be a name of a function/macro/special operator (unless it's an inner form of a macro, which introduces its own structure). So:
* (+ 1 2), not (1 + 2)
* (print 1) -- just like print(1)
* (with-open-file (in "test.txt") ...) -- a structural macro

2. If you want to pass a function somewhere #' is your best friend. Thus you can (fun)call it, apply it, map/filter with it etc.

3. Finally we need to add one more ingredient to the recipe -- the ability to use (lambda ...) forms in function position. We can do it with such a reader macro:
(set-dispatch-macro-character #\# #\f
(lambda (stream subchar arg)
(declare (ignore subchar)
(ignore arg))
(let ((sexp (read stream t nil t)))
(let ((fname (gensym)))
(setf (symbol-function fname) (eval `(lambda ,@sexp)))
fname))))

which allows us to write this code:
CL-USER> (#f((a b) (+ a b)) 1 2)
3