Здравствуйте, konsoletyper, Вы писали:
K>>Ну, можно чего попроще. Лексер написать. Кстати, сначала ты скажи, сколько у тебя это времени заняло, а потом я отвечу и мы сравним.
K>Ой, забыл сказать. Лексер C#. А то лексер вообще можно и за пару минут написать.
Не С# конечно , но просто как иллюстрация
class Parser extends StdTokenParsers with ImplicitConversions {
// Fill in abstract defs
type Tokens = Lexer
val lexical = new Tokens
// Configure lexical parsing
lexical.reserved ++= List("true", "false", "null")
lexical.delimiters ++= List("{", "}", "[", "]", ":", ",")
// Define the grammar
def root = jsonObj | jsonArray
def jsonObj = "{" ~ repsep(objEntry,",") ~ "}"
def jsonArray = "[" ~ repsep(value, ",") ~ "]"
def objEntry = stringVal ~ ":" ~ value ^^ { case x ~ y => (x,y) }
def value: Parser[Any] = (jsonObj | jsonArray | number | "true" ^^ true | "false" ^^ false | "null" ^^ null | stringVal)
def stringVal = accept("string", {case lexical.StringLit(n) => n})
def number = accept("number", {case lexical.NumericLit(n) => n.toDouble})
}
Re[13]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, lomeo, Вы писали:
K>>Ну, можно чего попроще. Лексер написать. Кстати, сначала ты скажи, сколько у тебя это времени заняло, а потом я отвечу и мы сравним.
L>Для лексера необходимости в макросах нет. Погляди Parsec.
Уже давно глядел. Не понравилось. Во-первых, все эти <###> смотрятся коряво, а хотелось бы человеческого EBNF-образного синтаксиса. Кроме того, мнее вообще непонятно, как это всё работает. Для этого надо сначала монады вкурить (а я этого не осилил), да потом ещё придумать, как извратиться, чтобы на монадах всё это построить. А на макросах я генератор лексера без какой-либо серьёзной математики сделал. Вон, на C++ тоже есть boost::spirit. И вот после мне говорят, что нафиг нужны макросы? А спрашивается, если их уже давно эмулируют через левое плечо на монадах и шаблонах, так почему бы не ввести в язык сами макросы. Даже если согласиться с утверждением, что макросы порождают кривизну, то отсюда следует, что эмуляция макросов породит её большую кривизну.
... << RSDN@Home 1.2.0 alpha rev. 672>>
Re[14]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, konsoletyper, Вы писали:
L>>Для лексера необходимости в макросах нет. Погляди Parsec.
K>Уже давно глядел. Не понравилось.
Давай не будем это считать аргументом.
K>Во-первых, все эти <###> смотрятся коряво, а хотелось бы человеческого EBNF-образного синтаксиса.
Синтаксис очень близок к EBNF вплоть до переименования.
Вместо "|" здесь "<|>", вместо +/* — many1/many, вместо '(' — char '(', "abc" — string "abc".
parseValue = choice [litString, litInt, litBool, parseList]
-- можно и ближе к EBNF: litString <|> litInt <|> litBool <|> listParser
parseList = do char '['
elems <- parseValue `sepBy1` listSep
char ']'
return (TList elems)
listSep = do skipSpaces
char ','
skipSpaces
skipSpaces = skipMany space
и т.д.
Помимо этого есть expression parsers, ещё более облегчающие описание.
K>Кроме того, мнее вообще непонятно, как это всё работает.
Это тоже вряд ли сойдёт за аргумент
K>Для этого надо сначала монады вкурить (а я этого не осилил), да потом ещё придумать, как извратиться, чтобы на монадах всё это построить. А на макросах я генератор лексера без какой-либо серьёзной математики сделал.
Поверь, для этого не надо вкуривать монады. Достаточно понять, что парсер это функция, дальше всё просто.
Да и вообще — у тебя уже есть готовый DSL. Зачем тебе знать на чём он построен — на макросах или монадах?
K>Вон, на C++ тоже есть boost::spirit. И вот после мне говорят, что нафиг нужны макросы? А спрашивается, если их уже давно эмулируют через левое плечо на монадах и шаблонах, так почему бы не ввести в язык сами макросы. Даже если согласиться с утверждением, что макросы порождают кривизну, то отсюда следует, что эмуляция макросов породит её большую кривизну.
Так в том то и дело, что макросы не надо эмулировать, если язык позволяет обойтись без них.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[14]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, Gaperton, Вы писали:
_>>Раз так, то может приведете пример задачи, которую Вы не умеете решать без использования макросов?
G>единственный известный мне пример, 100 %оправдывающий макрогенерацию — библиотека работы с матрицами, которая об'единяет циклы в алгебраических выражениях. Получается что понятный код не жертвует скоростью, в той задаче, где скорость критична. По сути, здесь идет речь о языковом расширении.
Можно подробнее о выделенном? Почему этого можно добиться только макросами?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[6]: Являются ли макросы свидетельством недостаточной выра
Здравствуйте, VladD2, Вы писали:
EC>>Я не предлагал заниматься метапрограммированием используя систему типов.
VD>Меж тем замена макросов в Хаскле получается только при использования метапрограммирования с использованием системы типов.
Речь не о замене. Речь о том, что необходимость в макросах отпадает, т.к. задача решается другим путём, не с помощью эмуляции макросов, что, конечно же, неествественно.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: Являются ли макросы свидетельством недостаточной выра
Здравствуйте, VladD2, Вы писали:
VD>Я вот все больше склоняюсь к тому, что простое решение — это решение описанное максимально близко к терминам предметной области (максимально высокоуронево). Ну, то есть с Виртом в корне не согласен. А макросы как раз позволяют мне встроить в язык такие прикладные решения. Системы типов этого не позволяют. Даже хаскелевская... если конечно не заниматься на ней МП.
Почему априори считается, что МП — это обязательно макросы?
Что касается встраивания в язык DSL, то на Haskell это получается замечательно. Просто там другой подход, и, разумеется, есть отличия от подхода с макросами. Вот в этой статье, например сказано, что именно Haskell не умеет делать, в отличие от макросов, а что умеет. Но делает это по своему, и не выглядит неестественным.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[15]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, lomeo, Вы писали:
K>>Во-первых, все эти <###> смотрятся коряво, а хотелось бы человеческого EBNF-образного синтаксиса.
L>Синтаксис очень близок к EBNF вплоть до переименования. L>Вместо "|" здесь "<|>", вместо +/* — many1/many, вместо '(' — char '(', "abc" — string "abc".
Вот именно это и не нравится. Выглядит не по-BNF-ному.
K>>Кроме того, мнее вообще непонятно, как это всё работает.
L>Это тоже вряд ли сойдёт за аргумент
Нет, тут немножко другой оттенок. Как юзать, я понял. Мне непонятно, как это всё написано. А отсюда следует простейшее умозаключение, что чтобы понять, как подобные вещи вообще пишутся на Хаскелле, нужны жутко извращённые мозги. Типа, если что-то в этом роде захочет написать обычнй средний программист, он наткнётся на БОЛЬШИЕ проблемы, что отталкивает людей от написания чего-то макросозаменяющего. Вот на том же Nemerle я без всяких проблем написал макрос, генерирующий лексер. Причём если какие-то проблемы и были, то они чисто технического плана. Мозги извращать не приходилось.
L>Поверь, для этого не надо вкуривать монады. Достаточно понять, что парсер это функция, дальше всё просто. L>Да и вообще — у тебя уже есть готовый DSL. Зачем тебе знать на чём он построен — на макросах или монадах?
А если я сам захочу приделать подобную "фичу" к языку, мне не всё равно на чём её писать. Макрос — это легко. А вот монады понять тяжело. Боюсь даже, это не из-за самих монад, а из-за того, как их подают. Пытался читать "The Haskell Programmer’s Guide to the IO Monad". Споткнулся на естественных преобразованиях. Во-первых, в самой статье для объяснения естественных преобразований применяется жуткая нотация, которая не оговорена нигде вообще. Как прекрасно для статьи, претендующей на вводную. Во-вторых, я всё-же нашёл в других источниках более внятное определение естественных преобразований, но так и не увидел объяснения, что же это такое.
L>Так в том то и дело, что макросы не надо эмулировать, если язык позволяет обойтись без них.
Дык, а чем, скажем, шаблоны C++ хуже макросов? Я так понимаю,
ага. И каждый ваш новоопределенный макрос — каждый, делает этот язык все более навороченным — причем, без контроля и толковых спецификаций. Что может быть и ускорит разработку, когда вы работаете в одиночку, но приведет к катастрофе, если над проектом работает большая группа. Сразу напишете неподдерживаемый код, который потом в помойку пойдет через пару лет.
направлен не на макросы, а на метапрограмиирование вообще? Типа, любое добавление фичи в язык приводит к неподдерживаемому коду. А чем же в этом смысле монады, тьюринг-полные типы и шаблоны лучше?
... << RSDN@Home 1.2.0 alpha rev. 672>>
Re[7]: Являются ли макросы свидетельством недостаточной выра
Здравствуйте, lomeo, Вы писали:
L>Речь не о замене. Речь о том, что необходимость в макросах отпадает, т.к. задача решается другим путём, не с помощью эмуляции макросов, что, конечно же, неествественно.
Т.е. система типов Хаскелля по отношению к нетривиальным фичам не есть то же самое, что шаблоны в C++? А можно об этом поподробнее, на примерах? И подоступнее, а то там чёрт ногу сломит.
PS: а всякие ката-, хило- и прочие морфизмы, про которые Мейер писал, можно на одном только Хаскелле в общем виде для любых алгебраических типов реализовать, или тут надо привлекть что-то помощнее?
... << RSDN@Home 1.2.0 alpha rev. 672>>
Re[16]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, konsoletyper, Вы писали:
K>Вот именно это и не нравится. Выглядит не по-BNF-ному.
Без проблем! Меняем названия на EBNF-ные.
K>Нет, тут немножко другой оттенок. Как юзать, я понял. Мне непонятно, как это всё написано. А отсюда следует простейшее умозаключение, что чтобы понять, как подобные вещи вообще пишутся на Хаскелле, нужны жутко извращённые мозги.
Нужны мозги, готовые писать декларативно.
Если хочешь, я объясню на пальцах строение Parsec. Знание монад вряд ли тебе понадобится.
На самом деле, когда я с Лиспа перешёл на Хаскель, я тоже недоумевал, как же так? А где макросы? Поработав через некоторое время я почувствовал, что для большинства вещей они там не нужны. Макросы в Лиспе большей частью нужны были для подслащивания: реализации ленивости (эта часть выполняется только если...), более удобной записи вызовов (вместо всех этих lambda что то вроде карринга), и прочей кодогенерации (скажем куча определений вместо одного) и т.д.
Всё это есть в Хаскель задаром. Последнее делается разве что немного по другому.
K>Типа, если что-то в этом роде захочет написать обычнй средний программист, он наткнётся на БОЛЬШИЕ проблемы, что отталкивает людей от написания чего-то макросозаменяющего. Вот на том же Nemerle я без всяких проблем написал макрос, генерирующий лексер. Причём если какие-то проблемы и были, то они чисто технического плана. Мозги извращать не приходилось.
Ну тебе же пришлось сначала понять, что такое макрос. Это уже определённое извращение мозгов
Что осталось сделать сейчас — это чуть чуть поднапрячься и понять, что такое функция
K>А если я сам захочу приделать подобную "фичу" к языку, мне не всё равно на чём её писать. Макрос — это легко. А вот монады понять тяжело.
Их легко понять на самом деле. Если ты их ещё не понял, то когда грокнешь, поймёшь, что легко.
С макросами я тоже не сразу разобрался, например.
K>Боюсь даже, это не из-за самих монад, а из-за того, как их подают.
+1
K>Пытался читать "The Haskell Programmer’s Guide to the IO Monad". Споткнулся на естественных преобразованиях. Во-первых, в самой статье для объяснения естественных преобразований применяется жуткая нотация, которая не оговорена нигде вообще. Как прекрасно для статьи, претендующей на вводную. Во-вторых, я всё-же нашёл в других источниках более внятное определение естественных преобразований, но так и не увидел объяснения, что же это такое.
В этой статье много ТК. Для объяснения монад ТК не нужно. Представляй монаду как интерфейс для вычислений, этого вполне достаточно.
Для объяснения монад воспользуйся лучше другими статьями. Ссылки уже были, если не найдёшь, накидаю.
L>>Так в том то и дело, что макросы не надо эмулировать, если язык позволяет обойтись без них.
K>Дык, а чем, скажем, шаблоны C++ хуже макросов? Я так понимаю,
Тем, что они эмулируют макросы, IMHO.
K>направлен не на макросы, а на метапрограмиирование вообще? Типа, любое добавление фичи в язык приводит к неподдерживаемому коду. А чем же в этом смысле монады, тьюринг-полные типы и шаблоны лучше?
Или просто функции? Они же тоже добавляют фичу в язык?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: Являются ли макросы свидетельством недостаточной выра
Здравствуйте, konsoletyper, Вы писали:
L>>Речь не о замене. Речь о том, что необходимость в макросах отпадает, т.к. задача решается другим путём, не с помощью эмуляции макросов, что, конечно же, неествественно.
K>Т.е. система типов Хаскелля по отношению к нетривиальным фичам не есть то же самое, что шаблоны в C++? А можно об этом поподробнее, на примерах? И подоступнее, а то там чёрт ногу сломит.
Ты, наверное, насмотрелся на Олега
Система типов обычно используется для того, для чего она предназначена — для установки ограничений.
Например, можно построить вычисления, которые можно записать только в определённом контексте.
См. например STM Тут несколько статей
Ссылку даю, потому что первая статья для тех, кто Хаскель не знает.
Есть несколько операций readTVar, writeTVar, которые выполняются только в пределах монады STM.
По русски это означает, что объединять эти действия ты можешь только с такими же.
Для того, чтобы вычисление произвести есть операция atomically :: STM a -> IO a, которая проводит STM-акцию.
Никаким другим образом ты не сможешь записать или прочитать TVar-переменную.
Или ST-монада. Это монада состояния работает аналогично. Ты не можешь записать переменную в одном вычислении, а прочитать в другом. Смысл в том, что мы имеем императивный язычок, на котором пишем вычиление, снаружи выглядящее абсолютно referential transparent.
Или обычные IORef ссылки, работать с ними можно только в пределах IO-монады. Грязный код отделяется от чистого системой типов.
Она для этого и предназначена.
Такое возможно на макросах?
Если да, будет ли это так же легко как и здесь?
Это мы ещё не затрагивали классы типов. А там — см. библиотеки Generics, Dynamics и т.д.
Есть и более мощные системы типов — с dependent types.
В частности они позволяют ставить на код такие ограничения, как (из примеров)
-- при складывании списков должен получиться список длина которого будет равна сумме длин самих списков.
-- при сортировке гарантированно вернётся отсортированный список
-- умножать можно только матрицы кол-во столбцов одной из котороых равно кол-ву строк другой.
И всё это определяет компилятор. Это, кстати, можно сделать и на Haskell с MPTC/fundeps/GADT и ещё парочкой флажков, но выглядеть конечно не будет так красиво, как на языках, имеющих такую систему типов.
Как это сделать на макросах?
K>PS: а всякие ката-, хило- и прочие морфизмы, про которые Мейер писал, можно на одном только Хаскелле в общем виде для любых алгебраических типов реализовать, или тут надо привлекть что-то помощнее?
Здравствуйте, lomeo, Вы писали:
L>Нужны мозги, готовые писать декларативно. L>Если хочешь, я объясню на пальцах строение Parsec. Знание монад вряд ли тебе понадобится.
Ладно, сам разберусь, когда время придёт. Меня вообще смутила приставка "monadic".
L>Ну тебе же пришлось сначала понять, что такое макрос. Это уже определённое извращение мозгов
Не, самое интересное, что мозги извращать не приходилось. Помню, когда начинал, для понимания Паскаля мозги не напрягал. Потом то же было с C/C++/Java/C#. Позже не было никаих проблем с SICP. А вот Александреску я долго не мог понять. Монады не понял до сих пор.
L>Что осталось сделать сейчас — это чуть чуть поднапрячься и понять, что такое функция
Дык вроде и так понимаю.
L>В этой статье много ТК. Для объяснения монад ТК не нужно.
Ну, самые простые вещи понять можно без ТК, на интуитивном уровне. Но вот я видел такие выверты, что тут без ТК, ИМХО, не обошлось.
L>Для объяснения монад воспользуйся лучше другими статьями. Ссылки уже были, если не найдёшь, накидаю.
Спасибо, но я и так много чего с "Декларативного программирования" в избранное положил. Поищу, наверняка посты с ссылками там лежат.
L>Тем, что они эмулируют макросы, IMHO.
Нет, я хотел другое показать. Если так рассуждать (см. пост выше), то можно вообще в каменный век уйти. Другое дело, что некоторые языки реализуют идею не самым лучшим образом. Но я не про это.
L>Или просто функции? Они же тоже добавляют фичу в язык?
Ага, у меня, собственно, такой же вопрос. Вот Хаскель — продвинутый язык, и в нём можно во многих случаях сделать то, что в Лиспе делается исключительно на макросах. Но не всё. Так почему же Хаскелю не нужны макросы? Или что-то, что их может полноценно заменить.
... << RSDN@Home 1.2.0 alpha rev. 672>>
Re[11]: Являются ли макросы свидетельством недостаточной выр
Даже такой элементарный пример позволяет, в общем-то проиллюстрировать. Чуть более сложный
r := b - A*x;
мне уже расписывать лень.
Каким еще способом можно добиться того, чтобы DSL не проигрывал в производительности коду a la FORTRAN?
Лично мне очень интересно.
... << RSDN@Home 1.2.0 alpha rev. 677>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[13]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, FR, Вы писали:
G>>>разумеется не надо. Для того, чтобы паттерны были не нужны, их не надо встраивать в язык. Надо язык выразительный иметь. G>>>валяйте, давайте сюда пример, и я покажу, как его сделать без макросов и паттернов. Мне хватит паттерн-матчинга, атомов, и первоклассных функций.
M>>Реализуйте паттерн ОО, плиз.
FR>Вот на схеме без использования макросов http://okmij.org/ftp/Scheme/pure-oo-system.scm
А при чём тут schema? Про существование CLOS я знаю, что это можно сделать на схеме — тоже.
Речь шла о хаскеле.
Здравствуйте, konsoletyper, Вы писали:
K>Вот Хаскель — продвинутый язык, и в нём можно во многих случаях сделать то, что в Лиспе делается исключительно на макросах. Но не всё. Так почему же Хаскелю не нужны макросы?
Ну... Те кто делал Template Haskell по всей видимости думали, что Haskell макросы нужны.
... << RSDN@Home 1.2.0 alpha rev. 677>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[18]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, konsoletyper, Вы писали:
L>>Что осталось сделать сейчас — это чуть чуть поднапрячься и понять, что такое функция
K>Дык вроде и так понимаю.
Я к тому, что в ФП функция рассматривает несколько по другому нежели в "обычных языках". Это первоклассная сущность.
Один из ярких примеров — это представление данных функцией. Скажем множество обычно представляют как некий контейнер, хранящий разные элементы. Если при работе с множеством нам нет необходимости перечислять его элементы, а достаточно определять — входит ли элемент в множество, то это можно сделать так:
Мы определяем множество как функцию типа a -> Bool, т.е. функцию, которой передаётся элемент, а она возвращает True, если элемент принадлежит множеству.
type Set a = a -> Bool
Теперь мы можем определить добавление элемента в множество:
add :: a -> Set a -> Set a
add x set = \y -> if x == y then True else set y
пересечение множеств:
intersect :: Set a -> Set a -> Set a
intersect s1 s2 = \x -> s1 x && s2 x
объединение множеств:
union :: Set a -> Set a -> Set a
union s1 s2 = \x -> s1 x || s2 x
и т.д.
То же самое и со всем остальным. Поняв, что парсер (или, например, конечный автомат) это всего лишь функция, можно получить много полезных результатов в языке, где функция — первоклассный объект.
K>Ну, самые простые вещи понять можно без ТК, на интуитивном уровне. Но вот я видел такие выверты, что тут без ТК, ИМХО, не обошлось.
Глубоко же ты копал. А где, если не секрет?
L>>Тем, что они эмулируют макросы, IMHO.
K>Нет, я хотел другое показать. Если так рассуждать (см. пост выше), то можно вообще в каменный век уйти. Другое дело, что некоторые языки реализуют идею не самым лучшим образом. Но я не про это.
Я согласен, но спорить тут не возмусь — это вопрос того кто и как смотрит на такое понятие как чувство меры. Gaperton считает, что макросы нецелесообразны, а ты наоборот. Не думаю, что есть ответ на вопрос "кто из вас прав". А если и есть, то я его не знаю.
L>>Или просто функции? Они же тоже добавляют фичу в язык?
K>Ага, у меня, собственно, такой же вопрос. Вот Хаскель — продвинутый язык, и в нём можно во многих случаях сделать то, что в Лиспе делается исключительно на макросах. Но не всё. Так почему же Хаскелю не нужны макросы? Или что-то, что их может полноценно заменить.
Я не говорил, что не нужны. Иначе не было бы Template Haskell например. Я имел в виду, что некоторые вещи можно делать по другому. И получится не хуже.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[14]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, Klapaucius, Вы писали:
L>>Для лексера необходимости в макросах нет. Погляди Parsec.
K>Не подскажете где можно посмотреть сравнение производительности лексеров, написаных с помощью Parsec и других генераторов лексеров?
Не подскажу Я этим как то не интересовался.
В доке от 2001 года говорится, что
Speed. Most combinator libraries lack the speed necessary to be competetive with bottom-up parser generators. Parsec uses some novel techniques to improve its performance. The library is fast, parsing thousands of lines a second on today's machines
Но тысячи в секунду, это как то маловато, если честно. Даже для 2001. Очень-очень сомневаюсь, что это связано с тем, что реализация не на макросах. Надо с Happy сравнить. Полагаю, проблема всё таки в строках Haskell-а, которые как известно, медленные. Можно попробовать подключить binary string и померить.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[12]: Являются ли макросы свидетельством недостаточной выр
K>Даже такой элементарный пример позволяет, в общем-то проиллюстрировать. Чуть более сложный
Я не понял, если честно.
х у нас заранее создан с нужной длиной?
p, s — это матрица или вектора? alpha и omega — скаляры?
в чём прикол? почему нельзя просто перемножить и сложить? промежуточные структуры?
Можно сам макрос подсмотреть?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[11]: Являются ли макросы свидетельством недостаточной выр
Здравствуйте, lomeo, Вы писали:
L>Здравствуйте, Gaperton, Вы писали:
_>>>Раз так, то может приведете пример задачи, которую Вы не умеете решать без использования макросов?
G>>единственный известный мне пример, 100 %оправдывающий макрогенерацию — библиотека работы с матрицами, которая об'единяет циклы в алгебраических выражениях. Получается что понятный код не жертвует скоростью, в той задаче, где скорость критична. По сути, здесь идет речь о языковом расширении.
L>Можно подробнее о выделенном? Почему этого можно добиться только макросами?
В выражении a = b + c + d, где переменные — матрицы, надо, чтобы все это свернулось в единственный двойной цикл с этой формулой внутри. На плюсах это делается без макросов — через темплейтное метапрограммирование, и выглядит страшно. На макросах, мне думается, реализация выглядела бы проще. Хотя — честно скажу, пока на макросах реализации не видел. Все только говорят, говорят о великих и ужасных макросах — и все .