...

суббота, 15 августа 2015 г.

Как мы поднимали IT-инфраструктуру [со дна]

Всем здравствуйте!

Спустя ровно год после написания статьи «Опыт работы эникейщиком/системным администратором в бюджетной организации» и 2,5 лет после написании моим зам. директора подразделения статьи «Реанимация ИТ инфраструктуры» я хотел бы продолжить данный рассказ.

Помню, в одном из комментариев я встретил фразу:

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

Но, как ни странно, из того, что мы делали все-таки что-то получилось, и я хотел бы рассказать как далеко мы зашли:
  • создан домен Active Directory Domain Services с автоматическим управлением учетными записями и подразделениями (OU);
  • внедрен Office 365;
  • развернут Spacewalk (ПО для управления *nix операционными системами);
  • создан HA MySQL Server master-master (Active-Passive);
  • развернута хостинг-панель Ajenti;
  • настроен SSL доступ к веб-ресурсам компании;
  • мигрирован VMware vCenter с 4.0 на 5.1U3;
  • внедрен ESET NOD32 Business Edition ver. 5;
  • внедрена авторизации в сети на базе МСЭ Cisco ASA 5525-X NGFW c CDA;
  • разрешены проблемы с кондиционированием в серверной.

За прошедший год мы с новым коллегой ( astrike — у него Read-only аккаунт, поэтому статью пишем вместе), о котором была речь в прошлой статье, разграничили зоны компетенций и стали приводить ИТ инфраструктуру к приличному виду. Он занимается написанием скриптов автоматизации (в т.ч. и на PowerShell), настройкой синхронизаций, разверткой и поддержкой *nix серверов и всего, что с ними связано. Я же занимаюсь продуктами Microsoft Windows, MS SQL, Office 365, виртуализацией и сетями передачи данных (Ethernet, SAN).
Активное сетевое оборудование Cisco:
  • core: Catalyst 6509;
  • access: Catalyst 35XX;
  • WLAN — controller: 4404 и 50 AP.
До всех коммутационных идет 1G оптоволоконный кабель, который, в лучшем случае, подключен к каждому коммутатору в стойке, в худшем — к половине коммутаторов в стойке (оставшиеся коммутаторы подключены каскадно).

Серверная инфраструктура:

  • две 42U стойки с 11 серверами Sun Fire X4170M2, объединенных в кластер ESXi;
  • СХД Hitachi AMS 2100;
  • 2 рядных кондиционера APC InRow (без резервирования), выносные блоки которых в жару охлаждаются проточной водой из садовых распылителей.

Основной задачей на момент внедрения была автоматизация жизненного цикла электронной почтовой службы. Для этих целей мы воспользовались уже частично развернутым на предприятии программным продуктом Office 365. Сотрудники стали пользоваться корпоративной почтой, при чем достаточно много и часто (особенно, после подписания приказа о корпоративной почтовой системе).

Совместно с нашим 1С-ником мы настроили автоматическое создание учетных записей в AD. Данный процесс мы организовали следующим образом:

  1. сервер 1С выгружает данные о работниках и структуре организации (OU) в файл с определенной структурой.
  2. скрипт на PowerShell забирает данные и формирует файл с патчем (разницей между текущим состоянием AD и выгрузкой из 1C). Сводка из этого патча рассылается заинтересованным лицам (действия, затрагивающие некоторые руководящие должности, требуют ручного подтверждения). Затем ночью патч применяется: создаются новые и обновляются существующие OU; добавляются, актуализируются, активируются и деактивируются учетные записи (естественно, по подразделениям). Такой подход гарантируют, что случайно не будут (например, при ошибке в 1C) деактивированы все учетные записи (отдельная проверка на количество изменений в патче) или учетные записи руководящих должностей и системных администраторов;
  3. после заведения локальных учетной записи DirSync синхронизирует (вообще, он синхронизируется раз в полчаса) локальный AD с облачным Azure AD (AAD);
  4. затем скрипт лицензирует и настраивает параметры учетных записей в облачном AAD в зависимости от их групповой принадлежности.
  5. теперь пользователю требуется получить пароль от своей учетной записи. Для автоматизации выдачи паролей был написан специальный PowerShell скрипт и добавлен пункт меню «Распечатать новый пароль» в AD. При нажатии на него автоматически распечатывается пароль пользователя и инструкция по использованию информационных служб на принтере «по умолчанию» для данного пользователя (рисунок ниже). После печати все файлы удаляются.

В настоящее время продолжается процесс ввода ПК в домен. На текущий момент в домене уже более 150 ПК под управлением OC Windows 8.1. В дальнейшим планируется развернуть Windows 10 на всех ПК компании.Для именования ПК мы используем наклейки с шаблонным идентификатором компьютера (например, COMP-00045), которые клеим на ПК и регистрируем в домене.


В связи с наличием зоопарка *nix серверов, некоторые из которых уже вышли из поддержки (например, Ubuntu 10.04), а другие просто подпорчены, например, сборками пакетов через make install, было решено создать свой «золотой образ», на основе которого разворачивались бы новые *nix сервера. Мы решили, что для наших целей лучше всего подходит дистрибутив CentOS, так как у него есть большое количество структурированных мануалов, срок поддержки, а также достаточно консервативная политика. Для администрирования этих серверов было решено использовать Spacewalk. Spacewalk — это система управления *nix операционными с системами с поддержкой проксирования репозиториев (как WSUS на Windows), управления конфигурационные файлами, установленными пакетами и возможностью выполнения комманд на подключенных серверах.

В итоге, в VMware vSphere был создан шаблон (template) со скриптом, который вводит сервер в домен (для DNS и авторизации), настраивает конфигурацию сети и пользователей. Таким образом разворачивание новой системы свелось к следующему:

  1. создания виртуальной машины из шаблона в VMware vSphere;
  2. подключения к этой виртуальной машинке и запуск скрипта развертки (на bash), который назначает системе IP (на выбор статический или DHCP) и другие другие параметры (в т.ч. hostname), вводит сервер в домен AD, подключает сервер к Spacewalk, который сразу же устанавливает необходимые пакеты и разворачивает конфигурационные файлы;

Таким образом, получается новый *nix сервер с доменными политиками доступа (вход до доменной учетной записи), автоматическим обновлением Errata, настроенным мониторингом Zabbix, центрально-управляемыми репозиториями и конфигурационными файлами (Spacewalk).

Благодаря унификации дистрибутива сильно упростилось администрирование серверов (одинаковые команды, расположение конфигурационных файлов, единые репозитории и пакеты). А благодаря централизованный системе управления ОС Spacewalk конфигурационные файлы не теряются (и есть возможность обновления конфигурационных файлов сразу на всех серверах с подстановкой переменных), всегда видно какие сервера требуют обновления, а критические обновления автоматически устанавливаются.

В настоящее время осуществляются работы по переносу приложений (в основном, веб-приложений) на новые сервера и по разворачиванию новых сервисов.


Для непрерывной работы практически любого приложения требуется бесперебойная работа БД. А так как большинство существующих в компании приложений используют MySQL, то был развернут MySQL сервер (не MariaDB, так как в момент разворачивания у неё была какая-то ошибка, о который мне сообщил наш разработчик).

Непрерывность работы БД обеспечивается master-master репликацией по стратегии Active-Passive (балансировка нагрузки пока не актуальна), т.е. один сервер всегда является основным (Active), но при его падении (или падении MySQL сервера на нем) все запросы переходят на Passive сервер. При поднятии Active сервера все запросы снова переходят на него.

Такое переключение (failover) достигается благодаря использованию виртуального IP (VIP), которое обеспечивает демон keepalived. Использование данной технологии позволяет (в отличии от proxy-серверов) ограничивать доступ к схемам по IP и не создает лишнего хопа, следовательно и лишней задержки.

Из основных настроек БД, на мой взгляд, осталась только настройка LDAP авторизации. Конечно, можно и SSL настроить, но это уже после разворачивания центра сертификации (CA).


Так же за прошедший год мы смогли мигрировать кластер VMware vSphere с более чем 100 рабочими виртуальными машинами и настроить функции HA, DRS, SDRS и DPM, а на все гостевые операционные системы было установлено ПО VMware Tools, что позволило использовать виртуальную инфраструктуру на полную мощность.

В дальнейшем была настроена система бекапов с использованием средств Veeam Backup and Replication, которые на данный момент развернуты в триальном режиме, но уже настроены такие функции как автоматическая проверка бекапов в виртуальной лаборатории, служба VSS для Windows приложений. Как показала практика бекапы копии VM консистенты и стартует без проблем (тьфу, тьфу).

Так теперь выглядит процентное соотношение вирутальных машин в нашем кластрере. До конца года мы планируем полностью вывести из эксплуатации Windows Server 2003, а так же заменить старые Debian и Ubuntu на CentOS 7.


Для защиты рабочих станции мы используем антивирус ESET. За несколько лет эксплуатации достаточно хорошо себя зарекомендовал, а самое главное, что у него есть за хорошее централизованное управление на компьютерах вне домена. Мы развернули версию 5, так как примерно 400 компьютеров обращаются к managment-серверу ERA без установленных агентов (как это надо в версии 6), а для их установки желательно иметь домен для установки на них агентов ESET.
Мы создали в серверной холодные и горячие коридоры с минимальными затратами: стойки были накрыты монолитным поликарбонатом, а с помощью направляющих были сделаны двери для входа в коридоры.

В результате температура в серверной понизилась до 18 градусов в холодном и до 23 — в горячем, а также на 40% снизилась нагрузка на кондиционеры.


Для ограничения доступа в интернет сейчас развертываем систему авторизации в сети на базе Cisco ASA 5525-X и CDA сервера. Но пока все это в процессе. Конечно, хотелось бы сделать это с использованием IEEE 802.1X для всех устройств в сети, но как получится в итоге — неизвестно.
Как ни странно, но за этот год мы с другом смогли реализовать достаточно много крупных и интересных проектов, попутно закончив Университет. Сейчас идет процесс причесывания различных вещей, а так же написание регламентов и приказов по использованию сети, домена и т.д.

После этого останется выбрать и внедрить решения для организации HelpDesk`a, а так же развернуть некоторые продукты из линейки System Center Configuration Manager, Operations Manager и, возможно, Service Manager. Тогда почти все проблемы будут решены, кроме отсутствия крупных финансовых вливаний в ИТ-департамент, которые влекут за собой отсутствие денег на модернизацию серверов, коммутаторов и т.д, и повышение зарплат, но это уже совсем другая история…

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

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Таблицы сопряженности: лог-линейные модели и Марковские сети

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

Кратко о предыдущей серии. По ESS данным 2012 года для генеральной совокупности “Мужчины возраста 25-40 лет” была построена таблица о степени поддержки человеческих ценностей в каждой из стран опроса. Для понижения размерности представления матрицы размера 29х21, определяемой таблицей, было произведено NMF преобразование ранга 5. Повторю итоговую теплокарту позиционирования всех 29 стран в полученном пространстве, чтобы она была перед глазами

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

  • Россия и Словакия, по результатам иерархической кластеризации — соседи;
  • Франция и Россия, как варианты стран с различными представлениями.

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

Немного о таблицах сопряженности
Все ценностные переменные в таблице для выполнения NMF преобразования воспринимались как одна переменная со множественным выбором (multiple response variable). Это было необходимо для представления данных в виде двухмерной таблицы, то есть таблицы образованной двумя переменными. В действительности у нас ситуация несколько иная, полный набор из 21 ценностной переменной и 1 переменная указывающая страну определяют 22-мерную таблицу сопряженности.
Вероятно это покажется удивительным, но с точки зрения построения статистических моделей, многомерные таблицы сопряженности (c single response переменными и без пропущенных ответов) — более простая ситуация, нежели таблицы с multiple response переменными. Кроме того, с помощью NMF размерность таблицы была снижена до 6 — 5 латентных переменных + 1 переменная со страной.

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

Прежде чем перейти к вычислениям отметим, что в общем случае многомерные таблицы сопряженности определяют мультиномиальное распределение. Но когда маргинальные суммы этого распределения по одному измерению или нескольким измерениям фиксированы, получаем так называемое product-multinomial распределение. Поэтому требуется накладывать дополнительные ограничения на параметры лог-линейных моделей для таких таблиц. Подробности можно найти в главе 12 книги [1]. В нашем случае маргинальные суммы фиксируются по одному измерению — размеры генеральных совокупностей в каждой из стран являются константами. Это означает, что главный эффект отвечающий переменной со страной не может быть исключен из модели.

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

Определяем и сравниваем модели
По-прежнему используем пакет survey [2] среды R для учета эффектов стратификации, кластеризации и взвешивания выборки. Более подробно об этом сообщалось в одной из прошлых публикаций. Параметры лог-линейных моделей для complex survey данных ровно те же самые, что и для таблиц без учета дизайна исследования. Требуется корректировка формул вычисляющих значимость параметров модели (как в отдельности, так и в совокупности).

Загружаем данные, выделяем ген. совокупность, добавляем в базу латентные переменные и задаем дизайн исследования.
library(foreign)
library(data.table)
library(survey)

srv.data <- read.dta("ESS6e02_1.dta")
srv.variables <- data.table(name = names(srv.data), title = attr(srv.data, "var.labels"))
srv.data <- data.table(srv.data)
setkey(srv.data, cntry) 
setkey(srv.variables, name)

fr.dt<-data.table(read.dta("ESS6_FR_SDDF.dta"))
ru.dt<-data.table(read.dta("ESS6_RU_SDDF.dta"))
ru.dt[,psu:=psu+150]  # psu values are changed to avoid their intersections between countries
sk.dt<-data.table(read.dta("ESS6_SK_SDDF.dta"))

sddf.data <- rbind(fr.dt, ru.dt, sk.dt)
setkey(sddf.data, cntry, idno)

cntries.data <- srv.data[J(c("FR", "RU", "SK"))] 
cntries.data[ ,weight:=dweight*pweight]
setkey(cntries.data, cntry, idno )

cntries.data <- cntries.data[sddf.data] 
cntries.data <- cntries.data[gndr == 'Male' & agea >= 25 & agea<=40, ]

# add the latent variables<b> a.1, a.2, ..., a.5</b> to the cntries.data
answers <- c('Very much like me', 'Like me')
cntries.data[,a.1:= imprich %in% answers | ipsuces %in% answers]
cntries.data[,a.2:= ipgdtim %in% answers]
cntries.data[,a.3:= ipmodst %in% answers]
cntries.data[,a.4:= ipadvnt %in% answers | impfun %in% answers]
cntries.data[,a.5:= ipfrule %in% answers | ipudrst %in% answers]

# define survey design
srv.design.data <- svydesign(ids = ~psu, strata = ~stratify, weights = ~weight, data = cntries.data)
options(survey.lonely.psu="adjust")


Пример 1, простейший — таблица для России и Словакии с одной латентной переменной «money | success».

Строим две модели: предполагающую независимость факторов и насыщенную.

Вычисления показывают ...
ru.sk.data <- subset(srv.design.data, cntry %in% c("RU", "SK"))
srv.loglin.model.ind <- svyloglin(~a.1+cntry, ru.sk.data)
srv.loglin.model.sq <- update(srv.loglin.model.ind, ~.^2)
anova(srv.loglin.model.ind, srv.loglin.model.sq)


Analysis of Deviance Table
Model 1: y ~ a.1 + cntry
Model 2: y ~ a.1 + cntry + a.1:cntry
Deviance= 0.1240613 p= 0.4737981
Score= 0.1217862 p= 0.4778766

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

Пример 2. Рассмотрим таблицу со всеми пятью латентными переменными для Франции и России.
Лог-линейная модель, предполагающая попарную независимость всех факторов отвергается. Модель со всеми элементами второго порядка является приемлемой. Эту модель можно (и нужно) упростить — отбросить по результатам wald и likelihood ratio критериев, параметры второго порядка для переменной определяющей страну и последними двумя латентными переменными теплокарты.

Вычисления
fr.ru.data <- subset(srv.design.data, cntry %in% c("FR", "RU"))

srv.loglin.model.ind <- svyloglin(~ a.1 + a.2 + a.3 + a.4 + a.5 + cntry, fr.ru.data)
srv.loglin.model.sq <- update(srv.loglin.model.ind, ~.^2)
srv.loglin.model.tri <- update(srv.loglin.model.ind, ~.^3)
srv.loglin.model.four <- update(srv.loglin.model.ind, ~.^4)

anova(srv.loglin.model.ind, srv.loglin.model.sq)$dev$p[3]  #5.745843e-50
c( anova(srv.loglin.model.sq, srv.loglin.model.tri), anova(srv.loglin.model.sq, srv.loglin.model.four) ) #  0.7335668 0.7427429

sapply(paste('cntry:a.',1:5,sep=""), function(x) round(regTermTest(srv.loglin.model.sq, x)$p, 3) )


cntry:a.1 cntry:a.2 cntry:a.3 cntry:a.4 cntry:a.5
0.000 0.000 0.000 0.437 0.524
anova(update(srv.loglin.model.sq, ~. -cntry:(a.4 + a.5)), srv.loglin.model.sq)$dev$p[3]


0.6066181

Условная независимость. Почему математические способности и размер обуви — зависимые факторы?
Эта вариация на тему классического примера. Предположим, математические способности респондента определяются следующей градацией--- высокие, средние или низкие. Строим таблицу сопряженности с этими двумя переменными, скажем, для населения всей России. Гипотеза о независимости этих переменных смело может быть отвергнута. У людей с большим размером обуви выше математические способности. В чем причина? В отсутствии скрытой переменной — возраст. Ясно, что до определенного момента возраст положительно коррелирует как с математическими способностями, так и с размером обуви. Если фиксировать возраст (Age = k), то для любого k таблица совместного распределения величин M (мат. способности) и S (размер обуви) не будет указывать о наличии значимой зависимости между ними. В таком случае говорят, что величины M и S условно независимы. Этот результат выражается естественным образом в виде Марковской сети — ненаправленной графической модели.

Добавлю, что на Хабре есть отличная статья о Байесовских сетях — направленных графических моделях.

Графическое представление лог-линейных моделей
Предыдущий пример можно обобщить и распространить его на произвольные иерархические лог-линейные модели, что и было реализовано в работе [3]. Рассмотрим ряд возможных вариантов для трех переменных A, B и C.


Эти Марковские сети соответствуют следующим лог-линейным моделям

image

Заметим, что не всякая иерархическая лог-линейная модель может быть представлена в виде Марковской сети. Например — модель AB/AC/BC. Но любая модель может быть однозначно вложена в минимальную Марковскую сеть. Подробности соответствия лог-линейных и графических моделей можно найти в книге [1] или статье [3].

Итоговые результаты
Марковские сети позволяют относительно легко ориентироваться во взаимоотношениях переменных и сравнивать результаты различных таблиц.

Видим, что в случае России и Словакии наблюдается значимая взаимосвязь между страной и переменной «важен поиск приключений и риск или возможность повеселиться». С остальными ценностными качествами переменная Country условна независима.
Тогда как во Франции и России значимо различие в отношении к трем утверждениям: «важно быть богатым или иметь успех», «важно хорошо проводить время» и «важно быть простым и скромным».
Оба этих вывода согласуются с результатами теплокарты.
Что же касается взаимосвязи между латентными переменными, то графы для этих пар стран отличаются только одним ребром. Для России и Словакии переменные «важно хорошо проводить время» и «важно следовать правилам или важно помогать окружающим» условно независимы.

В заключение отмечу, что в лог-линейных моделях для complex survey данных пошаговый выбор модели, основанный на AIC или BIC результатах, пока не реализован. Статьи с адаптацией этих критериев к таким данным стали появляться только в последние годы. В частности, в этом году вышла статья [4], один из соавторов которой — T. Lumley, создатель пакета survey.

Литература:
[1] G. Tutz (2011) Regression for Categorical Data, Cambridge University Press.
[2] T. Lumley (2014) survey: analysis of complex survey samples. R package version 3.30.
[3] J. N. Darroch, S. L. Lauritzen, and T. P. Speed (1980) Markov fields and log-linear interaction models for contingency tables. Annals of Statistics 8(3), 522–539.
[4] T. Lumley, A. Scott (2015) AIC and BIC for modelling with complex survey data, J. Surv. Stat. Method. 3 (1), 1-18.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Mikrotik VRF+NAT — Управляем с одного хоста устройствами с одинаковыми IP-адресами

Недавно знакомый попросил помощи с настройкой микротика. Просьба была не совсем простая. Идея в том, что нужно было одновременно управлять с одного хоста четырьмя устройствами с неуправляемым TCP/IP стеком. На всех этих устройствах были одинаковые настройки IP, причем просто IP-адрес и маска, ни шлюз, ни DNS не указаны. Странная, но, как оказалось, весьма реальная ситуация. Не будем вдаваться в подробности причин невозможности перенастройки адресации на этих устройствах, а просто примем этот факт за аксиому. Задача поставлена так, как есть, и ее нужно решить.
Итак, исходные данные:
1. Четыре устройства с одинаковыми настройками IP — 192.168.1.1/24; GW и DNS не указаны; изменить эти настройки невозможно.
2. ПК, с которого необходимо одновременно иметь доступ ко всем четырем устройствам, допустим на WEB-интерфейс.
3. Простенький MikroTik RB750GL на 5 портов.

MikroTik RB750GL с настройками по умолчанию использует 1 порт для подключения к Интернету (NAT, FW), а остальные порты для подключения к локальной сети с настроенным DHCP — как обычный домашний роутер или роутер Small Business серии. Нам нужно задействовать все 5 портов, поэтому для начала полностью чистим конфиг и избавляемся от NAT, FW и DHCP.

Итак, кофиг очистили, собираем схему:
image

Теперь к делу…
Первая и основная проблема, которая встает перед нами — это использование одинаковых IP-адресов или IP-адресов из одной подсети на разных интерфейсах маршрутизатора для обеспечения сетевой доступности целевых устройств. Как трактуют основы сетевого взаимодействия с одной таблицей маршрутизации такое сделать невозможно. Значит надо сделать несколько таблиц маршрутизации, и в этом нам поможет Virtual Routing and Forwarding (VRF). Не будем сильно погружаться в VRF — нам достаточно просто поместить разные интерфейсы в разные таблицы маршрутизации:

/ip route vrf
add interfaces=ether1 routing-mark=DEV1
add interfaces=ether2 routing-mark=DEV2
add interfaces=ether3 routing-mark=DEV3
add interfaces=ether4 routing-mark=DEV4

Отлично. Теперь настроим IP-адресацию согласно схеме:
image

IP-адрес 192.168.2.1 будет использоваться для доступа к менеджменту самого MikroTik'а:

/ip address
add address=192.168.2.1/24 interface=ether5 network=192.168.2.0
add address=192.168.1.2/24 interface=ether1 network=192.168.1.0
add address=192.168.1.2/24 interface=ether2 network=192.168.1.0
add address=192.168.1.2/24 interface=ether3 network=192.168.1.0
add address=192.168.1.2/24 interface=ether4 network=192.168.1.0

Вспомним, что на самих устройствах, которыми нужно управлять, также отсутствует возможность настройки шлюза по умолчанию. Еще нам нужно как-то разделять эти устройства для доступа с MGMT PC. Естественно NAT. Для каждого устройства выделим IP из подсети 192.168.2.0/24 и настроим на интерфейсе ether5:

/ip address
add address=192.168.2.11/24 interface=ether5 network=192.168.2.0
add address=192.168.2.12/24 interface=ether5 network=192.168.2.0
add address=192.168.2.13/24 interface=ether5 network=192.168.2.0
add address=192.168.2.14/24 interface=ether5 network=192.168.2.0

Сам NAT пока не трогаем и вспоминаем, что наши пакеты должны «бегать» между разными таблицами маршрутизации. Для этого нужно ставить маршрутные метки согласно новым IP-адресам, которые в дальнейшем будем NAT'ировать. Согласно документации NetFilter таблица Mangle, которая отвечает за маркировку трафика, отрабатывает раньше таблицы NAT. Опираясь на этот факт делаем следующее:

/ip firewall mangle
add action=mark-routing chain=prerouting dst-address=192.168.2.11 new-routing-mark=DEV1
add action=mark-routing chain=prerouting dst-address=192.168.2.12 new-routing-mark=DEV2
add action=mark-routing chain=prerouting dst-address=192.168.2.13 new-routing-mark=DEV3
add action=mark-routing chain=prerouting dst-address=192.168.2.14 new-routing-mark=DEV4


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

/ip firewall mangle
add action=mark-routing chain=prerouting dst-address=192.168.2.2 new-routing-mark=main


На основе данного правила все пакеты с адресом назначения 192.168.2.2 будут перекладываться в основную таблицу маршрутизации «main», в которой и находится интерфейс хоста управления.

Осталось подумать о NAT. Для каждого устройства у нас будет по два правила:

/ip firewall nat
add action=dst-nat chain=dstnat dst-address=192.168.2.11 in-interface=ether5 to-addresses=192.168.1.1
add action=src-nat chain=srcnat out-interface=ether1 to-addresses=192.168.1.2
add action=dst-nat chain=dstnat dst-address=192.168.2.12 in-interface=ether5 to-addresses=192.168.1.1
add action=src-nat chain=srcnat out-interface=ether2 to-addresses=192.168.1.2
add action=dst-nat chain=dstnat dst-address=192.168.2.13 in-interface=ether5 to-addresses=192.168.1.1
add action=src-nat chain=srcnat out-interface=ether3 to-addresses=192.168.1.2
add action=dst-nat chain=dstnat dst-address=192.168.2.14 in-interface=ether5 to-addresses=192.168.1.1
add action=src-nat chain=srcnat out-interface=ether4 to-addresses=192.168.1.2

Таким образом с помощью NAT'а и VRF мы представили для управляющего хоста устройства с одинаковыми IP-адресами, как устройства с разными IP-адресами, а с помощью NAT'а на интерфейсах, которые смотрят в сторону этих устройств, позволили им работать без шлюза по умолчанию.
В итоге, для управления целевыми устройствами (например, через web-интерфейс) на управляющем хосте необходимо набрать в браузере:
DEV1 - http://192.168.2.11
DEV2 - http://192.168.2.12
DEV3 - http://192.168.2.13
DEV4 - http://192.168.2.14

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

пятница, 14 августа 2015 г.

[Из песочницы] Упрощаем for-цикл по индексам: range-based версия

Волею судеб мне довелось заняться одной задачей автоматизации при помощи Python-скрипта. Изучая базовые конструкции, наибольший интерес у меня вызвал следующий код:
for index in range(0,10) :
  do_stuff()


Удобно, читаемо, лаконично (модно, стильно, молодежно)! Почему бы не организовать такой же цикл в С++? Что из этого вышло — под катом.

Попытка первая — макросы


О недостатках макросов написано много. И главное правило гласит: «Если можно что-то реализовать не используя макросы — так и сделай». Но иногда использование макросов вполне оправданно.
Макросы часто используют для расширения языка нестандартными конструкциями — например, чтобы ввести ключевое слово вечного цикла для большей читаемости кода:
#define infinite_loop while(true)
infinite_loop 
{
  do_stuff();
}


Кстати, мы ведь тоже задались вопросом реализации нестандартного цикла. Что если попробовать реализовать это дело с помощью макросов. Примерно вот так:
#include <iostream>

#define ranged_for(var, min, max, step) for(auto var = (min); var < (max); var += (step) )
int main()
{
  ranged_for(i, 0, 10, 1) 
  {
    std::cout << i << std::endl;
  }
  return 0;
}


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

Кроме того есть и ряд других недостатков:

  • Нечитаемые имена. Макросы — это автозамена. Если использовать простые имена в названии и аргументах макроса, то велик шанс коллизий с пользовательским кодом. Показательный пример — коллизия макроса min\max из Windows.h с функциями стандартной библиотеки std::min\std::max. Поэтому часто приходится использовать нечитаемые имена во благо избежания описанной проблемы.
  • Никакой перегрузки. Макросы — это автозамена. Если написать несколько макросов с одинаковым именем, то доступен будет только один и з них. Поэтому написать несколько версий одного и того же макроса нельзя. А нам бы хотелось чтоб прям совсем как в Python.

Да и что уж тут говорить — это абсолютно не похоже на пример из Python.

Попытка вторая — функция-генератор коллекции


Если внимательно почитать документацию по range() из Python, то можно увидеть, что range() генерирует список сразу всех значений из диапазона. Поступим точно так же и напишем функцию, которая будет возвращать std::vector где каждый элемент — это значение индекса:
template<typename T>
std::vector<T> range(T min, T max, T step)
{
    const bool is_unsigned = std::is_unsigned<T>::value;
    if (is_unsigned && min > max)
        return std::vector<T>(0);

    size_t size = size_t((max - min) / step);
    if (!is_unsigned && size < 0)
        return std::vector<T>();
    if (size == 0)
        return std::vector<T>(1, min);

    std::vector<T> values;
    values.reserve(size);
    if (step < 0)
    {
        for (T i = min; i > max; i += step)
        {
            values.push_back(i);
        }
    }
    else
    {
        for (T i = min; i < max; i += step)
        {
            values.push_back(i);
        }
    }
    return values;
}

template<typename T>
std::vector<T> range(T min, T max)
{
    return range<T>(min, max, 1);
}

template<typename T>
std::vector<T> range(T max)
{
    return range<T>(0, max);
}


Учитывая новый синтаксис для перебора значений коллекции в стандарте С++11, возможно написать следующий код:
int main() 
{
    std::cout << '[';
    for (int i : range<int>(10))
        std::cout << i << ' ';
    std::cout << ']' << std::endl;
    
    std::cout << '[';
    for (int i : range<int>(0, 10))
        std::cout << i << ' ';
    std::cout << ']' << std::endl;

    std::cout << '[';
    for (int i : range<int>(0, 10, 2))
        std::cout << i << ' ';
    std::cout << ']' << std::endl;

    std::cout << '[';
    for (int i : range<int>(10, 2))
        std::cout << i << ' ';
    std::cout << ']' << std::endl;

    std::cout << '[';
    for (int i : range<int>(10, 2, -1))
        std::cout << i << ' ';
    std::cout << ']' << std::endl;
    return 0;
}


Вооот, это уже похоже на то, чего мы хотим достигнуть. Теперь это читается как «По всем i в диапазоне от 0 до 10». Согласитесь, звучит лучше, чем «От i равного 0, пока меньше 10, увеличивать на 1». В итоге вывод программы будет следующим:

[0 1 2 3 4 5 6 7 8 9 ]
[0 1 2 3 4 5 6 7 8 9 ]
[0 2 4 6 8 ]
[]
[10 9 8 7 6 5 4 3 ]

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

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

Попытка третья, финальная — псевдо-коллекция


Чтобы пользовательский класс-коллекция поддерживал проход с помощью range-based циклов необходимо всего нечего — реализовать функции begin() и end(), которые возвращают итераторы на начало и конец коллекции соответственно. Дополнительно необходимо реализовать класс самого итератора. Но что если реализовать класс, который коллекцией будет только на уровне интерфейса, но внутренняя реализация хранить все значения не будет, а сгенерирует их по мере необходимости?

Тогда упрощенная реализация нашего класса может выглядеть следующим образом:

template<typename T>
class range sealed
{
public:

    range(T _min, T _max, T _step = T(1))
      : m_min(_min), m_max(_max), m_step(_step)
    { }

    T operator[](size_t index)
    {
      return (m_min + index * m_step);
    }

    size_t size() 
    {
      return static_cast<size_type>((m_max - m_min) / m_step);
    }
    range_iterator<range<T>> begin() 
    {
      return range_iterator<range<T>>(this, m_min);
    }

    range_iterator<range<T>> end()
    {
      return range_iterator<range<T>>(this, m_min + size() * m_step);
    }

private:
    T m_min;
    T m_max;
    T m_step;
};


Все, что необходимо хранить — это границы диапазона и шаг. Тогда любой элемент диапазона можно получить с помощью простой арифметики (см. operator[]). Основная же работа возлагается на класс итератора:
template<typename T>
class range_iterator sealed
{
public:
    typedef T range_type;
    typedef range_iterator<range_type> self_type;
    typedef typename range_type::value_type value_type;

    range_iterator(const range_type* const range, value_type start_value)
        : m_range(range), m_value(start_value)
    { }

    operator value_type() const 
    { 
        return m_value; 
    }

    value_type& operator*() {
        return m_value;
    }

    self_type& operator++() {
        m_value += m_range->step();
        return *this;
    }

    self_type operator++(int) {
        self_type tmp(*this);
        ++(*this);
        return tmp;
    }

    bool operator==(const self_type& other) const {
        return ((m_range == other.m_range) &&
            (equals<value_type>(m_value, other.m_value, m_range->step())));
    }

    bool operator!=(const self_type& other) const {
        return !((*this) == other);
    }

private:
    template<typename R> static bool equals(R a, R b, R e) {
        return a == b;
    }

    template<> static bool equals(double a, double b, double e) {
        return std::abs(a - b) < std::abs(e);
    }

    template<> static bool equals(float a, float b, float e) {
        return std::abs(a - b) < std::abs(e);
    }

    const range_type* const m_range;
    value_type m_value;
};


Думаю, дополнительно стоит пояснить наличие функции equals(). Предположим у нас диапазон нецелочисленный, а, допустим, от 0 до 10 с шагом 0.1. Сравнение итераторов основано на сравнении текущих значений из диапазона, хранящихся в каждом из них. Но сравнивать числа с плавающей точкой в С++ просто так нельзя. Подробнее почему можно почитать вот здесь. Скажу лишь, что если сравнивать «в лоб», то скорее всего цикл будет бесконечным. Лучший способ — это сравнивать разницу с допустимой абсолютной погрешностью. Это и реализовано в функции equals(). При чем в нашем случае абсолютная погрешность — это шаг диапазона.

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

Полная версия кода:

range.h
template<typename T>
class range_iterator : std::iterator<std::random_access_iterator_tag, typename T::value_type>
{
public:
    typedef T range_type;
    typedef range_iterator<range_type> self_type;
    typedef std::random_access_iterator_tag iterator_category;
    typedef typename range_type::value_type value_type;
    typedef typename range_type::size_type size_type;
    typedef typename range_type::difference_type difference_type;
    typedef typename range_type::pointer pointer;
    typedef typename range_type::const_pointer const_pointer;
    typedef typename range_type::reference reference;
    typedef typename range_type::const_reference const_reference;

    range_iterator(const range_type* const range, value_type start_value)
        : m_range(range), m_value(start_value)
    { }

    range_iterator(const self_type&) = default;
    range_iterator(self_type&&) = default;
    range_iterator& operator=(const range_iterator&) = default;
    ~range_iterator() = default;

    operator value_type() const { 
        return m_value; 
    }

    value_type& operator*() {
        return m_value;
    }

    self_type& operator++() {
        m_value += m_range->step();
        return *this;
    }

    self_type operator++(int) {
        self_type tmp(*this);
        ++(*this);
        return tmp;
    }

    self_type& operator--() {
        m_value -= m_range->step();
        return *this;
    }

    self_type operator--(int) {
        self_type tmp(*this);
        --(*this);
        return tmp;
    }

    self_type operator+(difference_type n) {
        self_type tmp(*this);
        tmp.m_value += m_range->step() * n;
        return tmp;
    }

    self_type& operator+=(difference_type n) {
        m_value += n * m_range->step();
        return (*this);
    }

    self_type operator-(difference_type n) {
        self_type tmp(*this);
        tmp.m_value -= n * m_range->step();
        return tmp;
    }

    self_type& operator-=(difference_type n) {
        m_value -= n * m_range->step();
        return (*this);
    }

    bool operator==(const self_type& other) const {
        return ((m_range == other.m_range) &&
            (equals<value_type>(m_value, other.m_value, m_range->step())));
    }

    bool operator!=(const self_type& other) const {
        return !((*this) == other);
    }

private:
    template<typename T> static bool equals(T a, T b, T e) {
        return a == b;
    }

    template<> static bool equals(double a, double b, double e) {
        return std::abs(a - b) < std::abs(e);
    }

    template<> static bool equals(float a, float b, float e) {
        return std::abs(a - b) < std::abs(e);
    }

    const range_type* const m_range;
    value_type m_value;
};

template<typename T>
class range sealed
{
    static_assert(std::is_arithmetic<T>::value, "Template type should be a integral-type");

public:
    typedef T          value_type;
    typedef T*         pointer;
    typedef const T*   const_pointer;
    typedef T&         reference;
    typedef const T&   const_reference;
    typedef size_t     size_type;
    typedef ptrdiff_t  difference_type;
    typedef range<value_type>               self_type;
    typedef class range_iterator<self_type> iterator;
    typedef std::reverse_iterator<iterator> reverse_iterator;

    range(value_type _min, value_type _max, value_type _step = value_type(1))
        : m_min(_min), m_max(_max), m_step(_step) {
        if (m_step == 0) {
            throw std::invalid_argument("Step equals zero");
        }
    }

    range(const self_type&) = default;
    range(self_type&&) = default;
    range& operator=(const self_type&) = default;
    ~range() = default;

    bool operator==(const self_type& _obj) const {
        return (m_max == _obj.max()) &&
            (m_min == _obj.min()) &&
            (m_step == _obj.step());
    }

    bool operator!=(const self_type& _obj) const {
        return !(this == _obj);
    }

    value_type operator[](size_type _index) const {
#ifdef _DEBUG
        if (_index > size()) {
            throw std::out_of_range("Index out-of-range");
        }
#endif
        return (m_min + (_index * m_step));
    }

    bool empty() const {
        bool is_empty = ((m_max < m_min) && (m_step > 0));
        is_empty |= ((m_max > m_min) && (m_step < 0));
        return is_empty;
    }

    size_type size() const {
        if (empty()) {
            return 0;
        }
        return static_cast<size_type>((m_max - m_min) / m_step);
    }

    value_type min() const {
        return m_min; 
    }

    value_type max() const {
        return m_max;
    }

    value_type step() const {
        return m_step;
    }

    iterator begin() const {
        iterator start_iterator(this, m_min);
        return start_iterator;
    }

    iterator end() const {
        iterator end_iterator(this, m_min + size() * m_step);
        return end_iterator;
    }

    reverse_iterator rbegin() const {
        reverse_iterator start_iterator(end());
        return start_iterator;
    }

    reverse_iterator rend() const {
        reverse_iterator end_iterator(begin());
        return end_iterator;
    }

private:

    value_type m_min;
    value_type m_max;
    value_type m_step;
};

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

[Перевод] Внедрение компонентого подхода в вебе: обзор веб-компонент

Четыре из пяти самых запрашиваемых новых платформенных возможностей Edge на User Voice (Shadow DOM, Template, Custom Elements, HTML Imports) относятся к семейству API, называемых веб-компонентами (Web Components). В этой статье мы хотим рассказать о веб-компонентах и нашем взгляде на них, некоторой внутренней кухне, для тех, кто еще с ними не знаком, а также порассуждать на тему того, куда все это может эволюционировать в будущем. Это довольно-таки длинный рассказ, поэтому откиньтесь назад, возьмите кофе (или не кофеиновый напиток) и начинайте читать.

Содержание:

  • Внедрение компонент: старая практика проектирования, ставшая новой для веба
  • Как разбивать на компоненты
  • Это все не в первый раз: предыдущие подходы к внедрению компонент
  • Современные веб-компоненты
  • Веб-компоненты: следующее поколение

Внедрение компонент: старая практика проектирования, ставшая новой для веба


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

Изначально сложность веб-приложений в основном регулировалась со стороны сервера за счет разделения приложения на отдельные страницы, что требовало от пользователя соответствующим образом переходить в браузере с одной страницы на другую. С внедрением AJAX и связанных технологий разработчики смогли отказаться от потребности делать «переходы» между разными страницами веб-приложения. Для типичных сценариев вроде чтения почты или новостей ожидания пользователей изменились. К примеру, после логина в почту, вы можете «пользоваться почтовым приложением» с одного и того же адреса (URL) и находится на этой странице целый день (т.н. Single-Page Applications, SPA). Логика клиентских веб-приложений в таких ситуациях существенно усложняется, иногда она даже становится сложнее, чем на серверной стороне. Возможным разрешением данной сложности может являться дальнейшее разделение на компоненты и изоляция логики внутри одной страницы или документа.

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

Как разбивать на компоненты?


Так как веб-компоненты должны связать воедино HTML, CSS и JavaScript, необходимо учитывать существующие модели изоляции, присущие каждой из технологий, так как они влияют на сценарии и целостность веб-компонент. Эти независимые модели изоляции включают:
  • Изоляция стилей в CSS
  • JavaScript и области видимости (замыкания)
  • Изоляция глобального объекта
  • Инкапсуляция элементов (iframe)

Изоляция стилей в CSS


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

Внутри таблиц стилей CSS-правила применяются к документу, используя селекторы. Селекторы всегда рассматриваются как потенциально применимые ко всему документу, поэтому их область применения, в сущности, глобальная. Глобальное применение приводит к реальным конфликтам, когда несколько человек, работающих над проектом, смешивают вместе свои CSS-файлы. C пересечениями и повторениями селекторов можно бороться в четком порядке (например, каскады, специфичность, порядок следования исходников) для разрешения конфликтов, однако, такие действия, вполне вероятно, — совсем не то, чего хотели разработчики. Есть много потенциальных способов решения этой проблемы. Простое решение — перенести элементы и связанные стили, участвующие в формировании компоненты из основного документа в другой документ (теневой документ) так, что они больше не будут «реагировать» на чужие селекторы. Это приводит ко второй проблеме: теперь, когда мы их разграничили, как некоторый стиль может пересечь границу (для управления снаружи компоненты)? Очевидное возможное решение — это явно использовать JavaScript, но это выглядит как-то ужасно: полагаться на JavaScript для передачи стилей через границу, что кажется скорее пробелом в CSS.

Чтобы передать стили через границу компоненты эффективным образом и при этом защитить структуру компоненты (например, разрешить свободу изменения структуры без влияния стилей), существует два общих подхода, к которым многие склоняются: «частичная» стилизация с использованием псевдо-элементов и кастомные свойства (ранее известные как «переменные» CSS). Какое-то время также рассматривался супер-мощный кросс-граничный селектор '>>>' (определен в CSS Scoping), но сегодня он общепризнан не самой удачной идеей, так как легко нарушает изоляцию компонент.

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

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

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

Другие подходы к изоляции CSS стилей


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

CSS предлагает некоторые ограниченные формы изоляции селекторов в специфичных сценариях. Например, правило @‍media группирует набор селекторов вместе и применяет их при наступлении условий, соответствующих медиа-контексту (например, размер или разрешение вьюпорта, или медиа-тип — печать и т.п.); правило @‍page определяет некоторые стили, которые применимы только в контексте печати; правило @‍supports объединяет вместе селекторы для применения только, когда реализована поддержка специфичной CSS-функциональности — новая форма определения наличия функциональности в CSS); предложенное правило @‍document группирует селекторы для применения только тогда, когда документ, в котором загружены стили, соответствует условиям.

Области видимости в CSS (изначально написанные как часть работы над веб-компонентами) предлагают способ ограничивать применимость CSS-селекторов внутри одного HTML-документа. Спецификация вводит новое правило @‍scope, которое позволяет селектору определить корень(и) области применения и далее приводит к тому, что применение всех селекторов внутри правила @‍scope будет работать только в поддереве этого корны (а не на всем документе). Спецификация позволяет указывать корень области декларативно в HTML (например, предложен <style scoped> атрибут, пока реализованный только в Firefox; эта функциональность ранее была доступна в Chrome в качестве экспериментальной, но после была целиком удалена). Некоторые аспекты этой функциональности (к примеру, :scope, определенный в Selectors L4) также могут применяться для относительной оценки селекторов в новом API запросов в спецификации DOM.

Тут важно отметить, что @‍scope устанавливает только одно-направленную изоляцию границ: селекторы, содержащиеся внутри @‍scope, ограничены этой областью, в то время как любые другие селекторы (вне @‍scope) могут спокойной проникать внутри @‍scope (хотя они могут быть по-разному упорядоченным каскадом стилей). Это несколько неудачный дизайн, так как он не предоставляет ограничения области и изоляции от любых стилей, которые не находятся в подмножестве @‍scope — весь CSS должен по-прежнему «хорошо стыковаться», чтобы избежать стилизации внутри чужого правила @‍scope. См. также набросок @‍in-shafow-of от Таба, который лучше согласован с моделью защиты изоляции компонент.

Другое предложение ограничения видимости — это сдерживания в CSS. Сдерживание области видимости — это в меньшей степени про изоляцию стилей и селекторов и в большей про изоляцию «композиции». Внутри свойства «contain» поведение некоторых возможностей CSS, которые имеют естественное наследование (в смысле применимости от родительского к дочернему элементу в документе, например, счетчики) будет блокировано. Основное применение этого для разработчиков состоит в том, чтобы указать, что некоторые элементы предполагают строгое «сдерживание», так что композиция, применимая к этому элементу и его поддереву никогда не будет затрагивать композицию других элементов документа. Эти обещания сдерживания (указываемые применением свойства «contain») позволяют браузерам оптимизировать композицию и отрисовку так, что «новая» композиция удерживаемого поддерева потребует только обновления этого поддерева, а не всего документа.

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

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

JavaScript и области видимости


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

Лексические области видимости в JavaScript – это однонаправленная изоляция границ: код внутри области может иметь доступ как ко внутреннему содержимому, так и к содержимому любой родительской области вплоть до глобальной, в то время как код снаружи области не имеет доступа к ее содержимому. Важным принципом является то, что однонаправленный способ изоляции отдает предпочтение коду внутри области, то есть защищает его. Код внутри лексической области имеет возможность защищать/прятать себя от остального окружения (или не делать этого).

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

Изоляция глобального объекта


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

Есть ряд соображений, которые нужно учитывать при проектировании компонент с поддержкой изоляции глобального объекта – особенно если изоляция будет подразумевать защищенные границы (подробнее ниже). На сегодня мы ожидаем, что изолированные компоненты не будут полностью доступны до тех пор, пока базовый набор спецификаций веб-компонент не будет зафиксирован (то есть, это «отложено до следующей версии»). Однако, если мы потратим некоторое время на исследование того, как изолированные компоненты могут выглядеть, это может направить в правильное русло текущую работу. На некоторые предложения действительно стоит обратить внимание.

Изоляция глобального объекта – это важный нереализованный сценарий для веб-компонент. А пока мы работаем над реализацией, можно, например, полагаться на самый успешный и распространенный на сегодня способ привнесения компонентности в веб: iframe-элемент.

Инкапсуляция элементов (iframe)


Iframe-элементы и их близкие родственники: элементы object, frameset и императивный API windows.open() – уже предоставляют возможность работать в изолированным поддеревом элементов. Однако, если компоненты подразумевают работу внутри одного документа, iframe включает внутри себя целый HTML-документ; как если бы два отдельных веб-приложения были размещены совместно, просто одно внутри другого. У каждого уникальные адрес документа, глобальное окружение для скриптов и область видимости CSS; каждый документ полностью отделен от другого.

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

  • JavaScript-код внутри одно HTML документа может потенциально вторгнуться в границы изоляции другого документы (например, через свойство contentWindows у iframe элемента). Такая возможность нарушения границы может быть необходимой потребностью, но она также представляет собой риск с точки зрения безопасности, когда содержимое iframe содержит чувствительную информацию, которой не хотелось бы делиться. Сегодня нежелательные нарушения могут регулироваться политиками общего источника: документы с URL из одного источника могут нарушать границы по умолчанию, в то время, как документы из разных источников имеют ограниченные возможности взаимодействия друг с другом.
  • Нарушение границы – это не единственный риск безопасности. Использование атрибута <iframe sandbox> накладывает дальнейшие ограничения на iframe из других источников с тем, чтобы защитить хост-окружение от нежелательных скриптов, всплывающих окон, изменения навигации и других возможностей, доступных в iframe.
  • CSS-стили внешнего документы не могут применяться к внутреннему документу. Такое архитектурное решение следует принципу изоляции. Однако изоляция стилей создает существенный пробел в интеграции iframe в качестве компоненты (в рамках общего источника происхождения). HTML адресует эту проблему с помощью предложенного атрибута <iframe seamless> для iframe с общим источником. Такой атрибут «бесшовности» удаляет изоляцию стилей контента фрейма; бесшовно включенные документы берут копию стилей хостового документа и отображаются, как если бы их ограничений iframe-элемента, в который они включены, не было.

С хорошими политиками безопасности и возможность вставлять фрейм бесшовно, использование iframe в качестве модели для компонент кажется весьма привлекательным решением. Однако нескольких свойств желательных в модели веб-компонент все же не хватает:

  • Глубокая интеграция. Iframe ограничивает (и в основном полностью отключает) интеграцию и взаимодействие моделей между хостом и фреймовым документом. К примеру, относительно хоста: фокус и модель выделения независима, а передача событий изолирована одним или другим документом. Для компонент, которые предполагают более близкую интеграцию, поддержка такого поведения не возможная без внедрения некоторого «агента» в хостовом документе, который бы пробрасывал сведения через границу.
  • Размножение глобальных объектов. Для каждой сущности iframe, созданной на странице, будет присутствовать уникальный глобальный объект. Глобальный объект и связанная с ним полная система типов не дешева в создании и может привести к потреблению большого количества памяти и чрезмерной нагрузки на браузер. Множественные копии одного и того же компоненты, используемые на одной странице, не обязательно должны быть изолированы друг от друга, на практике наличие общего глобального объекта может быть желательно, особенно, если они должны поддерживать некоторое общее состояние.
  • Модель использования контента хоста. Iframe не позволяет повторного использования контентной модели хост-элемента внутри фреймового документа. (Для простоты: контентная модель элемента – это его поддерживаемое поддерево элементов и текста.) К примеру, select-элементы имеет контентную модель, включающую option-элементы. Select-элемент, реализованный в виде компоненты, захочет некоторым образом взаимодействовать с дочерними элементами.
  • Выборочная стилизация. Бесшовный iframe не работает с документами из разных источников. Есть явные риски безопасности, если бы это было разрешено. Основная проблема в том, что «бесшовность» контролируется хостом, а не фреймовым документом (фреймовый документ чащей является жертвой атак). Для компоненты двузначная возможность включения «бесшовности» (есть или нет) может быть слишком дорогостоящей; компоненты скорее хотели бы выборочно принимать решение, какие стили от хоста применимы к их содержимому (вместо автоматического наследования всех стилей, то есть это то, как работает бесшовность). В целом, вопрос, что должно стилизоваться, должен разрешаться самой компонентой.
  • Выставление API. Многие сценарии для веб-компонент подразумевают создание полноценных кастомных элементов с собственными выставленным набором API, семантикой отображения и управлением жизненным циклом. Использование iframe ограничивает разработчика до работы в рамках API iframe, со всем его особенностями. К примеру, вы не можете влиять на параметры самого iframe и его жизненный цикл.

Это все не в первый раз


Не можем не отметить, что в прошлом несколько технологий уже было предложено и даже внедрено в попытке улучшить работе iframe в HTML и связанные возможности инкапсуляции. Впрочем, ни одна из них не прижилась в значительной степени в современном вебе:
  • HTML Компоненты (1998) были предложены и внедрены Microsoft, начиная с IE5.5 (устарели в IE10). Предполагалось использование декларативной модели для добавления событий и API к хост-элементу (с учетом изоляции) и парсинга компонент в некоторый “viewlink” (как “теневой DOM”). Были доступны поведения компонент: одно временно присоединялось к элементу, другое динамично привязывалось через CSS-свойство “behavior”.
  • XBL (2001) и его наследник XML2 (2007) были предложены Mozilla как дополнение к из языку XUL для описания интерфейсов. Это декларативный язык с двумя возможностями связывания (похоже на HTML Компоненты от Microsoft), XML также поддерживал дополнительные возможности доступа к контентной модели хоста и генерации контента.

Современные веб-компоненты


После провала первых двух попыток, пришло время попробовать запустить игру в компоненты снова, на этот раз делом занялся Google. Используя концепции, описанные в XBL в качестве стартовой точки, монолитная компонентная система была разбита на коллекцию строительных блоков для компонент. Эти строительные блоки позволили веб-разработчикам экспериментировать с отдельными полезными возможностями до того, как общее видение для веб-компоненты будет полностью определено. Компонентность самого подхода и разработка отдельных полезных возможностей позволили продвинуться ближе к успеху. Практически каждый может найти в веб-компонентах что-то полезное для своего приложения!

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

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

Первое поколение веб-компоненты включает:

  • Кастомные элементы (Custom Elements). Кастомные элементы определяют точку расширения HTML-парсера, чтобы он мог распознавать имена новых «кастомных элементов» и предоставить им автоматически необходимую объектную модель на JavaScript. Кастомные элементы не создают границ компонент, но хато предоставляют браузеру способ присоединять API и поведения к авторским элементам. Браузеры, не поддерживающие кастомные элементы, могут их симулировать (через полифилы) с некоторой точностью, используя события и наблюдатели за изменениями и подстраивая прототип. Правильное планирование и понимание последствий – это ключевой элемент наших предстоящих встреч.
    • Аттрибут “is”. Он спрятан внутри спецификации кастомных элементов, но предоставляет критичную функциональность – возможность указать, что встроенный элемент должен получить имя кастомного элементы и новые API. В обычном случае кастомный элемент имеет в основе некий общий элемент; с помощью “is” за основу может быть взять нативный элемент (например, <input is=”custom-input”>). Хотя эта функциональность представляет отличный способ унаследовать преимущества от встроенного рендеринга, доступности, парсинга и т.п., синтаксис этой функциональности воспринимается скорее как хак, и есть мнение, что, возможно, примитивы для доступности и стилазация нативных элементов – это более подходящий в долгосрочном плане путь стандартизации.
  • Теневой DOM (Shadow DOM). Предоставляет императивный API для создания отдельного дерева элемента, которое может быть подсоединено (одноразово) к хост-элементу. Такие «теневые» потомки заменяют «реальных» потомков при отображении документа. Теневой DOM также предоставляет механизм использования контентной модели хост-элементы, используя новые slot-элементы (предложенные недавно), решает задачу с целевым элементом у событий и добавляет открытые/закрытые режимы операций (также недавно добавленные). Эта относительно тривиальная идея имеет удивительно большое количество сторонних эффектов во всем, начиная с модели фокуса и выделения и заканчивая композицией и распространения (для теневого DOM внутри теневых DOM).
    • Области видимости CSS определяют различные псевдо-элементы, релевантные стилизации теневого DOM, включая :host, ::content (скоро, возможно, станет ::slot), и бывший “>>>” (пронизывающий комбинатор теневого DOM), который теперь официально дезавуирован.
  • Template элемент. Он включен для полноты картины, эта функциональность ранее была частью веб-компонент, а сейчас является частью рекомендации HTML5. Шаблонный элемент вносит концепцию инертности (дочерние элементы шаблона не приводят к загрузке ресурсов, не реагируют на пользовательский ввод и т.п.). Это способ декларативно создать в HTML несвязаннное поддерево элемента. Шаблон может использоваться для разных задач: от собственно образцов шаблонов и связывания данных до предоставления контента для теневого DOM.
  • HTML Imports. Определяет декларативный синтаксис для «импорта» (запрос, загрузка или парсинг) HTML в документ. Запросы на импорт (используя link-элемент с rel=”import”) выполняют скрипты импортируемого документа в контексте хостовой страницы (таким образом, имея доступ к тому же глобальному объекту и состоянию). HTML, JavaScript и CSS части веб-компоненты могут, соответственно, быть загружены одним использованием импорта.
  • Кастомные свойства. Как описано детальнее выше, кастомные свойства, описанные вне компоненты и доступные для использования внутри компоненты – это простая и удобная модель для стилизации компонент сегодня. Учитывая это, мы включили кастомные свойства в первое поколение технологий для веб-компонент.

Веб-компоненты: следующее поколение


Как мы отметили в начале данной статьи, построить полнофункциональные веб-компоненты – это большое приключение. Несколько идей для развития и заполнения пробелов в текущем поколении возможностей уже начало циркулировать среди разработчиков (это не полный список!):
  • Декларативный теневой DOM. Он становится важным, когда вы задумываетесь, как передавать компоненты в сериализованном виде. Без декларативной формы, техники вроде innerHTML или XMLSerializer не смогут построить строкового представления DOM, включающего любой теневой контент. Другими словами, теневой DOM сегодня не может конвертироваться в строку и обратно без помощи скрипта. Anne из Mozilla предложил <shadowdom> элемент в качестве темы для обсуждения. Аналогично template-элемент уже является декларативным способом построения «теневой» разметки и методы сериализации в браузере уже подстроились под учет этой возможности и, соответственно, сериализуют «теневой» контент шаблона правильным образом.
  • Полностью изолированные компоненты. Три производителя браузеров сделали три разные предложения в этой области. Эти предложения уже достаточно хорошо согласованы, что является хорошей новостью с точки зрения достижения консенсуса. Как упоминалось ранее, изолированные компоненты будут использовать новый глобальный объекты и смогут быть импортированы из других источников. Они также будут иметь разумную модель длял выставления API и связанных поведений через свою границу изоляции.
  • Примитивы доступности. Многие сообщества обеспечения доступности симпатизируют идее “is” (из кастомных элементов) в качестве способа расширения существующих нативных элементов, так как они уже содержат механизмы обеспечения доступности, не всегда доступные JavaScript-разработчикам. В идеале, обычные веб-компоненты (без использования “is”) могли бы включать аспекты доступности практически также, как и нативные элементы, среди прочего, включая отправку форм и возможность фокусировки. Такие точки расширения не возможны сегодня, но должны быть проработаны и определены.
  • Единая стилизация нативных элементов управления. Пробел в наличии модели стилизации элементов управления консистентной между браузерами является проблемой интероперабельности, мешающей, например, распространению простых расширений «изменения темы». Это приводит к тому, что разработчики зачастую рассматривают теневой DOM как альтернативное решение (хотя создание разметки в теневом DOM с поведением аналогичным таковому у нативных элементов, может быть нетривиальным). Некоторые идеи стилизации элементов управления гуляют в CSS-сообществе, хотя без большого спроса они не имеют нужной скорости в проработке.
  • Частичная стилизация CSS. Хотя кастомные свойства CSS могут предоставить широкий набор опция для стилизации веб-компонент, существуют дополнительные сценарии, в которых выставление для стилизации некоторых блоков, зашитых внутри компонент, должно быть более прямым и сделано более подходящим способом. Это будет особенно удобно, если также рационализирует подход, которые существующие браузеры используют для выставления частичной стилизации нативных элементов управления.
  • Кастомизация парсера. Когда кастомные элементы используются, только стандартные теги открытия и закрытия могут использоваться для парсинга таких элементов. К некоторых сценариях может быть желательным разрешить дополнительные опции для парсера веб-компонент.

Наконец, хотя это и не считается официально частью веб-компонент, старый добрый iframe не должен списываться со счетов. Как обсуждалось, iframe – это все еще очень часто используемая функциональность веб-платформы, подходящая для создания сущностей, похожих на компоненты. Было бы полезным понять и потенциально улучшить «компонентную» историю iframe. К примеру, дальнейшее изучение и адресация проблем с <iframe seamless> кажется хорошей темой для начала дискуссии.

Веб-компоненты – это поворотный моменты для веба. Мы рады продолжать поддерживать и вносить свой вклад в это приключение. Делитесь вашим мнением с нами в твиттере @msedgedev.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

[Перевод] Краткая история появления Mathematica


Перевод поста Стивена Вольфрама (Stephen Wolfram) "There Was a Time before Mathematica…".
Выражаю огромную благодарность Кириллу Гузенко KirillGuzenko за помощь в переводе.

Через несколько недель [пост был написан 6 июня 2013 г. — прим. ред.] будет двадцатипятилетний юбилей: 23 июня 1988 года — день, когда была выпущена Mathematica.

Поздней ночью мы все ещё записывали дискеты и упаковывали их. Однако уже в полдень 23 июня я был в конференц-центре в Санта-Кларе, впервые показав публике Mathematica:

Mathematica 1.0 на Macintosh

Да, именно таким был загрузочный экран, и да, Mathematica 1.0 работала на маках и различных рабочих станциях на Unix; PС тогда не хватало мощности.

Многие оказались под впечатлением от того, что может делать Mathematica. И были очень приятные речи о перспективах Mathematica от различных лидеров компьютерной индустрии, в числе которых был и Стив Джобс (тогда он был в NeXT'е), который был весьма любезен, чтобы прийти, хоть он и не появлялся на публике в течение некоторого времени. А кто-то на этом мероприятии был достаточно дальновиден, чтобы попросить всех выступающих расписаться на книге о Mathematica, которая только поступила в продажу в тот день в книжных магазинах по всей стране:


Столько всего произошло с Mathematica за прошедшую с тех пор четверть века. То, что началось с Mathematica 1.0, превратилось в обширную систему, в сегодняшнюю Mathematica. И вот, я смотрю на юбилейный альбом в честь 25-летия, и меня берёт гордость от осознания того, какой вклад внесла Mathematica в исследования, изобретения, образование, науку:

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

Хорошо, но откуда взялась Mathematica? Как она стала такой, какая она есть? Это очень долгая история, правда. И тесно переплетена с моей жизнью. Однако, когда я пытаюсь заглянуть в будущее, мне интересно понять, как и какими путями всё менялось в прошлом.

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

Первые электронные калькуляторы начали входить в обиход, когда мне было 12, и они меня увлекли с головой. И примерно в то же время я начал пользоваться своим первым компьютером — штуковиной размером с большой стол, в котором памяти было 8 «килослов» по 18 бит каждое, запрограммированных по большей части на ассемблере с использованием бумажной перфоленты. Я пытался приспособить его каким-нибудь образом к решению физических задач, но без особого результата. И вот, мне уже 16, я опубликовал несколько статей по физике, окончил среднюю школу и работал в британской правительственной лаборатории. «Настоящие» физики-теоретики тогда практически не использовали компьютеры. А я использовал. То настольный калькулятор HP (с плоттером!), то мэйнфрейм IBM на Фортране.

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

И я начал размышлять, что с этим можно сделать. Мне представилось, как я всю жизнь буду гоняться за минусами и множителями в уравнениях. А потом я начал думать о том, как тут можно задействовать компьютер. И кто-то мне сказал, что есть и другие люди с подобными идеями. Были три программы, о которых я смог узнать, и как выяснилось, все они получили своё начало после одной беседы в ЦЕРНе, которая случилась в 1962 году: это Reduce (написана на LISP'е), Ashmedai (написана на фортране) и SCHOONSCHIP (написана на ассемблере 6000 CDC).

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

А затем, летом 1977 года я открыл для себя ARPANET — то, что в настоящее время переросло в Интернет. Тогда в сети было всего 256 хостов. И вот, @O 236 достался открытому компьютеру в Массачусетском технологическом институте, который работал с программой Macsyma, и в ней можно было проводить алгебраические вычисления в интерактивном режиме. Я был поражен тем, насколько мало людей пользовались этим. Но это было незадолго до тех времён, когда я стал большую часть времени тратить на это. В основном я работал с машиной методом тыка — сделав что-то, смотрел, что произойдёт, возвращался назад, опять что-то пробовал и т. д. И обычно делал такие странные вещи, как перечисление различных алгебраических форм интеграла, чисто экспериментально получая, какая продифференцирована корректно.

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

Затем был ноябрь 1979-го. Мне было 20, и я только что получил PhD в области физики. Я проводил несколько недель в ЦЕРНе, планируя своё будущее в физике (так мне тогда представлялось). И я тогда кое-что понял: чтобы продуктивно заниматься физикой, мне нужно нечто большее, чем Macsyma. И через некоторое время я решил, что единственный способ получить то, я хочу — сделать это самому.

И я начал реализовывать то, что потом стало называться SMP (Symbolic Manipulation Program, программа для символьных операций). Я обладал обширными познаниями в языках программирования того времени, как в сфере процедурных ALGOL'оподобных, так и в LISP и APL. И когда я набросал примерный вид SMP, то получилось нечто очень похожее на то, что я видел в этих языках. Однако по мере того, как я понимал, какой должна быть SMP, я начал пытаться придумывать всё сам.

Полагаю, у меня были некоторые весьма интересные идеи. И, по сути, в некоторых идеях из проектной документации ещё весьма сырой SMP прослеживаются мотивы грядущей Mathematica:

Просматривая сейчас документацию SMP, я понимаю, что это было весьма впечатляющая система, особенно учитывая, что мне было всего лишь 20 лет, когда я начал её проектировать. Разумеется, далеко не все идеи в SMP оказались хороши. И, как специалист в области дизайна языков с большим стажем, я не могу не упомянуть некоторые из моих «любимых» ошибок, которые представлены в конце этого поста.

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

Тогда я работал в Калифорнийском технологическом институте. И я пригласил выступить с речью всех, кого только мог найти в мире, кто бы работал над схожими системами. Я собрал небольшую рабочую группу в Калифорнийском технологическом институте, в которую некоторое время входил и Ричард Фейнман. И я начал вербовать людей со всего кампуса на работу над “SMP Project”.

Вначале предстояло принять важное решение о том, на чём писать SMP. Macsyma была написана на LISP'е, и многие говорили, что только на нём и можно написать. Но молодой аспирант-физик по имени Роб Пайк убедил меня, что C — язык будущего, что это правильный выбор (Роб много чем занимался впоследствии, включая создание языка Go). И вот, в начале 1980 года были написаны первые строки кода C для SMP.

Группа, работающая над SMP, имела весьма интересный состав. Первый из тех, кого я пригласил в группу — Крис Коул — после этого работал IBM и стал ярым последователем APL, после чего основал довольно-таки успешную компанию под названием Peregrine Systems. Были студенты, каждый из которых имел какие-то свои навыки, профессор, весьма увлечённый программированием, в соавторстве с которым я написал несколько статей по физике. Конечно, случались и оказии. Был один парень, который писал очень эффективный код, притом в одну строчку; он использовал красочные имена для функций, притом так, чтобы чтобы их комбинация читалась бы как какая-нибудь шутка. Или, скажем, был блестящий студент-бакалавр, который так усердно работал над проектом, что сильно отстал по учёбе; он пообещал не прикасаться к компьютеру, но вскоре был обнаружен за тем, что надиктовывал своему товарищу код.

Я и сам написал много кода для SMP (около 1000 строк каждый день). Я придумал её дизайн. И я написал большую часть документации. Я никогда до этого не управлял большим проектом. Однако, по какой-то причине, это никогда не было для меня чем-то очень сложным. И вот, в июне 1981 года, появилась первая версия SMP, и была она весьма похожей на Mathematica:

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

Я понял, что всё может иметь символьное представление. Хоть и символьно индексированные списки в SMP не позолили ввести «заголовки выражений», которые весьма ясно реализованы в Mathematica. И, несомненно, были некоторые странности во внутренней реализации символьных выражений, одна из которых — хранение всех чисел в формате с плавающей запятой (Тини Вельтман, автор SCHOONSCHIP, а после и лауреат Нобелевской премии по физике, сказал мне, что хранение числа в формате с плавающей запятой было одним из лучших когда-либо сделанных им решений, потому что FPU намного быстрее в арифметических вычислениях, нежели ALU).

До SMP я написал много кода для таких систем, как Macsyma, и я понял, что всё, что я пытался сделать, заключалось в том, чтобы сказать системе: «у меня есть выражение, которое выглядит вот так, и я хочу его привести его вот к этой форме». Таким образом, в разработке SMP использование правил преобразования для семейств символьных выражений, представленных шаблонами, стало одной из центральных идей. Реализация, конечно, не была столь же чистой, как в Mathematica, и в ней имелись не самые удачные идеи. Однако там были уже многие ключевые элементы.

В конце концов, содержание документации у SMP первой версии 1981 года было в достаточной степени современным:

Да, графическому выводу отводится небольшой раздел — так же, как и управлению памятью. И есть такие очаровательные разделы, как “тупиковые ситуации в программе” (т. е. зависание системы), а так же “создание статистических выражений” (т. е. создание случайных выражений). Однако уже имеется «параллельная обработка», наряду с «построением программ» (т. е. генерация кода). SMP мог даже генерировать C код, компилировать его, и, что весьма пугающе, динамично связывать его с запущенным исполняемым файлом SMP. И было много математических функций и математических операций, хотя и значительно меньше, чем в Mathematica.

Ну, хорошо. Заработал SMP 1.0. И что с ним делать? Было очевидно, что для многих людей система оказалась бы полезной. Она запускалась на довольно больших компьютерах — на так называемых «мини-ЭВМ», наподобие VAX, который был по размерам в несколько больших холодильников, и стоили такие по несколько сотен тысяч долларов. Однако я знал, что было много научно-исследовательских и проектных организаций, которые имели подобные машины.

Тогда я ничего не знал о компаниях и бизнесе. Но я понимал, что работа людей над SMP должна быть оплачиваемой, и весьма очевидный вариант по получению денег заключался в продаже копий SMP. Моя первая мысль была пойти в организацию, которую сейчас назвали бы «центром обмена технологиями» в Калифорнийском технологическом институте и узнать, смогут ли они нам чем-то помочь. В то время офис по существу состоял из одного приятного пожилого джентльмена. Но после нескольких попыток стало ясно, что он на самом деле не знал, что нужно делать. Я спросил его о том, как такое может быть; я полагал, что подобные вещи должны в Калтехе происходить постоянно. «Ну, — сказал он, — в основном люди с факультета просто уходят и сами основывают свои компании, так что мы оказываемся никак не вовлечены в процесс». «О, — сказал я, — так я тоже могу так?». И, пролистав устав университета, он сказал: «программное обеспечение можно запатентовать, университет не претендует на авторские права, так что да, Вы можете».

И я так и сделал, решив основать свою компанию. Но всё оказалось не так просто. Потому что некоторое время спустя администрация университета вдруг решила, что всё было не OK. Были весьма странные и недостойные моменты («возьмите меня в долю, и я отпишусь от этого», и т. д.). Ричард Фейнман и Мюррей Гелл-Манн заступились от моего имени. Президент университета, похоже, не знал, что делать. И на какое-то время всё застопорилось. Но в итоге мы договорились, что университет будет предоставлять лицензии независимо от имеющихся у них прав, даже если им придётся поменять что-то в своих документах касательно программного обеспечения.

В итоге осталась одна загвоздка в виде тогдашнего ректора университета. Он заявил, что подобная лицензия создаёт конфликт интересов между университетом и компанией, если я работаю в университете и владею частью компании. «Хорошо, — сказал я, — это не проблема, я покидаю университет». Казалось, это стало большим сюрпризом. Но я ушёл из университета и перешел в Институт перспективных исследований в Принстоне, куда, как отмечал тогдашний директор, они отдали компьютер, когда умер Джон фон Нейман, так что они могли особо не волноваться касательно интеллектуальной собственности.

В течение многих лет я размышлял о том, что же тогда на самом деле происходило в Калтехе. И, так получилось, что буквально пару недель назад я решил снова посетить Калтех (получить награду “выдающегося выпускника”). Обедая в факультетской столовой, я обнаружил, что за соседним столиком сидел никто иной, как бывший ректор Калтеха, которому уже почти 95. Я был впечатлён его глубоким отзывом на то, что он назвал “the Wolfram Affair” (был ли он предупреждён?). В итоге наш разговор многое прояснил.

Честно говоря, всё оказалось ещё более странным, чем я мог себе вообразить. История в некотором смысле началась в 1930-е годы, когда Арнольд Бекман был в Калтехе, изобрёл измеритель рН, а затем покинул университет, основав Beckman Instruments. К 1981 году Бекман был основным спонсором Калтеха и председателем его попечительского совета. Между тем, председатель его биологического факультета (Ли Гуд) изобрёл геномный секвенсор. Он сказал, что много раз пытался им заинтересовать Beckman Instruments, но безрезультатно, и потому основал свою собственную компанию (Applied Biosystems), которая стала весьма успешной. В какой-то момент, как мне сказали, Арнольд Бекман расстроился и сказал администрации, что они должны “прекратить уходы IP с кампуса”. И так получилось, что это совпало со временем появления моего проекта по SMP. И тогдашний ректор сказал, что он обязан этим заняться (он так же был и химиком, которого Фейнман и Гелл-Манн, как физики, называли «околофизиком»).

Но, несмотря на весь этот кавардак, всё таки появилась компания, которую я назвал Computer Mathematics Corporation. В то время я всё ещё представлял себя молодым учёным и не думал, что буду управлять компанией. Так что я привёл CEO, который был старше меня где-то в два раза. И по велению директора и некоторых венчурных капиталистов компанию решили объединить со старпапом, который, как они думали, должен был преуспеть в НИОКР в сфере искусственного интеллекта.

Между тем, SMP продвигался под слоганом «математика на компьютере»:

Были ужасные промахи. Генеральный директор: «Давайте построим рабочую станцию для запуска SMP»; я: «Нет, мы софтверная компания, и я видел систему Stanford University Network (SUN), которая заведомо лучше всего того, что мы смогли бы сделать». А потом были подобные необоснованные заявления от рекламщиков:

И скоро я понял, что всё это неправильно. SMP получается какой-то дойной коровой, и хотя CEO не был особо хорош в зарабатывании денег, он был весьма хорош в их привлечении — пройдя через целый ряд успешных инвестиционных витков, пока в конце концов не пришёл к посредственным IPO много лет спустя.

Тогда лично для меня были потрясающие времена — я непосредственно занимался наукой, открывал вещи, которые впоследствии заложили основы для нового вида науки (A New Kind of Science). И по сути SMP оказался базой для того, что я в итоге сделал. Потому что мой принцип поиска вычислительных примитивов для SMP навёл меня на мысль о поиске вычислительных примитивов в природе и созданию новой науки из изучения результатов действий этих примитивов.

Возможно, вы спросите, что же случилось с SMP. Она по-прежнему продавалась — до тех пор, пока не вышла Mathematica. Ни строчки её кода не использовалось для Mathematica. Но иногда я запускал её лишь чтобы посмотреть, как она смотрится рядом с ней. Со временем становилось всё труднее найти машину, на которой бы запускалась SMP. И лет 15 назад последний наш компьютер, способный её запускать, ушёл на покой.

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

Прошло много лет, и мне очень хотелось снова увидеть SMP в действии. Так что вот такая задача. Вот исходник программы на C, зашифрованный так же, как и исходники SMP. На самом деле, это исходники программы, которая и произвела шифрование — версия circa-1981 Unix crypt utility, по-хитрому модифицированная путём изменения ряда параметров и тому подобного. Сможет ли кто-нибудь взломать шифр, освободив, наконец, SMP из столь долгого заточения? Вот что говорит Wolfram | Alpha Pro, если мы просто загрузим этот файл в неизменном виде:

Но вернемся к основной истории. Я перестал работать над SMP в 1983 году, занимаясь параллельно фундаментальной наукой, программными проектами, и своим (весьма поучающим) «хобби» в разработке технологий и стратегическом консалтинге. Понемногу я использовал SMP, но затем я, в конечном итоге, стал писать на C, как правило, склеивая алгоритмы, графики и интерфейсы.

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

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

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

И, конечно, результатом стала Mathematica.

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

Если это не было бы реализовано в SMP, я допустил бы множество ошибок. Однако SMP многому научил меня, показав, что важно, а что нет, в чём заключаются основные проблемы. Просматривая сейчас свои архивы, я вижу кропотливый процесс решения головоломок из проблем, с которыми я сталкивался при разработке SMP. И шаг за шагом приходило решение.

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

Первые фрагменты кода Mathematica были написаны в октябре 1986 года. И к середине 1987 года Mathematica начинает претворяться в жизнь. Я решил, что документация должна быть опубликована в виде книги, и сотни страниц были уже написаны. И я прикинул, что Mathematica 1.0 будет готова к апрелю 1988 года.

Первоначальный мой план для нашей компании заключался в сосредоточении на исследованиях и разработке, а распространять Mathematica предполагалось прежде всего через производителей компьютеров. Стив Джобс первым принял Mathematica «на борт», которая должна была поставляться на всех, пока ещё не выпущенных компьютерах NeXT. Затем последовали сделки с Sun, Silicon Graphics, IBM и рядом других компаний. Мы начали отправлять бета-версии Mathematica.. И пусть это было задолго до появления всемирной паутины, знание о существовании нашей системы начало распространяться. Также подключились некоторые СМИ (я до сих пор люблю это мороженое):

Где-то весной 1988 года мы официально установили 23 июня датой выхода Mathematica (не имея Wolfram|Alpha, я тогда и не знал, что это был день рождения Алана Тьюринга). Нужно было много чего ещё сделать. В те дни выпуск программного обеспечения представлял собой не просто щелчок переключателя. Вспоминается, как мы запускали The Mathematica Book в печать. Как я прилетел в Канаду с жестким диском, лично нянчившись с фотонаборной машиной все выходные, передав затем произведённую ею коробку плёнок человеку, встречавшему меня в Бостонском аэропорту. Но, несмотря на всё это, незадолго до 23 июня были разосланы эти вот загадочные приглашения:

И вот, полдень 23 июня, в помещении полно народу, и мы выпускаем Mathematica в свет.

Славные 25 лет были после этого. Основа, которую мы заложили в Mathematica 1.0, была хорошо подкреплена моим предыдущим опытом, и оказалась невероятно прочной, и мы на этой основе непрестанно продолжали её строить. Мой «план Б» по использованию Mathematica для своих научных исследований работал просто великолепно, и привёл меня в итоге к новому виду науки. И вместе с Mathematica мы смогли построить прекрасную компанию, а так же создать такие вещи, как Wolfram|Alpha. И в течение последующих 25 лет мы с большим удовольствием и гордостью наблюдали вклад Mathematica в самых различных направлениях.


Дополнительный материал: Уроки SMP


Какой была SMP? Вот несколько примеров программ в SMP, которые я написал для документации SMP:







В некоторых моментах они очень похожи на программы в Mathematica, вместе с обозначениями [...] для функций, {...} для списков и -> для правил замены. Но в них нет той читаемости, которая есть в Mathematica, и программы на SMP выглядят значительно менее понятными.

Одна из наиболее очевидных проблем заключается в том, что код в SMP забит символами $ и %, являющимися соответственно префиксами шаблонов и переменных. В SMP я ещё не реализовал идею, которая появилась в Mathematica, о отделении шаблонов (например, _ ) от их имён (например, x). И я считал, что важно подчеркнуть, какие из переменных являются локальными, однако мне не приходила в голову идея о выделении их цветом.

В SMP я уже реализовал (весьма полезную) идею разграничения непосредственного (=) и отложенного (:=) присваивания. Однако, уподобляясь алголоподобным языкам, я реализовал их тогда не особо ясно — : и ::. (Для правил замены форма -> была именно той формой, которая затем была реализована в Mathematica, а форма --> была аналогична современной :>. Конструкция S[...] была аналогична современной /.)

В SMP, так же, как и в Mathematica, встроенные функции писались с заглавной буквы (в то время это было чем-то новым — разделение на верхний и нижний регистры на компьютере). Но в то время как в Mathematica, как правило, используются обычные английские слова для обозначения функций, в SMP используются короткие и зачастую малопонятные аббревиатуры. Когда я работал над SMP, я находился под влиянием дизайна Unix и использовал принятые в нём короткие имена. И всё было бы в порядке, если в SMP было бы всего несколько функций. Но с сотнями функций, имена которых подобны чему-то вроде Ps, Mei и Uspb, в итоге сильно затрудняется читабельность кода. Конечно, тут играет роль ещё тот факт, что многие пользователи не умеют быстро печатать, так что это был ещё один голос в пользу коротких имён функций.

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

Были и значительно более тонкие ошибки. Как, к примеру, чрезмерное усердствование в обобщении систем. Как и в Mathematica, в SMP списки обозначились так: {a,b,c}. Функции имели такое обозначение: f[x]. И в своём стремлении достичь максимально возможной унификации я решил попробовать объединить понятия списков и функций.

Скажем, есть у нас список v={a,b,c} (в SMP присвоение обозначалось как :, так что записывалось бы вот так v:{a,b,c}). Затем, например, v[2] в SMP извлекало бы второй элемент списка. Но это обозначение выглядит так же, как если бы мы запросили значение функции v с аргументом 2. И это навело меня на мысль о том, что можно обобщить списки таким образом, чтобы у них было не только целочисленное количество элементов, а любое, которое можно выразить в символьной форме.

В SMP шаблонные выражения (x_ в Mathematica) писались так: $x. То есть при определении функции f[$x]:$x^2 можно представить, что это просто определение f как списка с символьной индексацией: {[$x]: $x^2}. Если нужно узнать то, как была определена функция, то достаточно просто ввести её имя — f, к примеру. И получаемое значение было бы символьно индексированным списком, которое бы и представляло собой определение.

Обычный список векторного типа может рассматриваться как нечто-то вроде {[1]:a, [2]:b, [3]:c}. И можно было перемешивать символьные индексы: {[1]: 1, [$x]:$xf[$x-1]}. Была также определенная унификация по части нумерации для обобщённых символьных выражений. И всё, казалось бы, хорошо. И чтобы описать свою единую концепцию функций и списков, я назвал f в f[x] проекцией, а x — фильтром.

Но постепенно эта концепция начала расходиться по швам. Получались разного рода странности при введении определений наподобие v[2]:b, v[3]:c. Тогда для v, согласно принципам SMP, будут значения {[3]:c, [2]:b}. Но что, если сделать определение v[1]:a? В этом случае v вдруг изменяет свой порядок на {a, b, c}.

И всё становилось ещё более странным при использовании функций от нескольких аргументов. Было бы здорово, если бы можно было определить матрицу с m:{{a,b},{c,d}}, тогда m[1] будет {a,b}, а m[1,1] или m[1][1] будет a. Но что делать, если нужна функция с несколькими аргументами? Будет f[x, y] тем же самым, что и f[x][y]? Ну, иногда требовалось нечто подобное, а иногда нет. Так что мне пришлось придумать свойство («атрибуты» в Mathematica), которое я назвал Tier, в котором для каждой функции указывалось, как она должна работать (сегодня многие, наверное, слышали о каррировании, но тогда подобные различия совсем не казались очевидными.)

Символьно индексированные списки в SMP обладали некоторыми изящными и мощными свойствами. Но в результате, когда вся система была построена, обнаружилось множество странностей. И потому, когда я разрабатывал Mathematica, я решил их не использовать. На протяжении многих лет я продолжал о них размышлять. И так получилось, что сейчас, более чем через 30 лет после SMP, я работаю над некоторым весьма интересным новым функционалом в Mathematica, который тесно связан с символьно индексированными списками [в 10-й версии системы появились ассоциации — Association — прим. ред.].

Работа над SMP дала мне бесценный опыт: я увидел, что действительно работает, а что нет. И одним из наиболее запоминающихся моментов для меня был этот. Как и в Mathematica, в SMP были чистые функции. Но, в отличие от Mathematica, они не указывались с помощью символа &. И это означало, что требовался специальный объект, называемый «mark» (пишется как `, машинописный обратный апостроф), для обозначения того, когда чистая функция должна выдавать некоторое символьное, константное значение. Ну, спустя лет пять после выхода SMP, я просматривал одно из обучающих пособий. И мне в глаза бросилась фраза: «обратные апострофы — загадка SMP». И тогда я понял: вот как выглядят ошибки в дизайне языка.

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

Один из важных моментов в проектировании системы — знать, что в ней будет важно. В SMP мы потратили много усилий на так называемые «семантические шаблоны». Скажем, есть у нас такое определение: f[$x+$y, $x, $y] := {$x, $y}. Вполне понятно, что ему будет соответствовать f[a+b, a, b]. Но что насчёт f[7, 3, 4]? В SMP шаблон будет удовлетворен, хотя 7 не есть форма конструкции $x+$y. Потребовалось большое количество усилий для реализации этого. И в простых примерах всё выглядело здорово. Но, в конце концов, подобное не требуется уж очень часто, а если и требуется, подобное стараются избегать, потому что такие конструкции сильно ухудшают читабельность программы, становится трудно понимать, что она делает.

Было нечто подобное с контролем рекурсии. Я думал, что данные конструкции стоит избегать f[$x] : $xf[$x-1] (без задания f[1]), ведь они имеют бесконечный цикл, пытаясь вычислить f[-1], f[-2] и т. д., пока, в конце концов, в какой-то момент не произойдёт умножение на 0. Так почему бы просто не выдать нуль? Ну, в SMP по умолчанию выводился нуль. Потому что вместо того, чтобы проходить весь путь по вычислению каждой ветви дерева рекурсии, SMP периодически останавливался и пытался упростить все невычесленные ветви. Это было умно и изящно. Однако когда кто-то пытался задать подобное поведение, всё становилось слишком сложным для понимания, и в итоге никто это не использовал.

А потом был определяемый пользователем синтаксис. Он позволял, к примеру, установить "U" (скажем, для " union"), как инфиксный оператор. И всё работало замечательно, пока кто-нибудь не вводил функцию с именем «u». Или пока кто-нибудь не становился пленником своего собственного синтаксиса.

SMP многому меня научил. И Mathematica не была бы так хороша, если бы ей не предшествовала SMP. И вот, размышляя сейчас об ошибках в SMP, меня радует тот факт, что, пожалуй, все эти ошибки остались в прошлом. После SMP и 25 лет работы над дизайном Mathematica большинство из них попадают для в категорию простеньких задачек.

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

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.