Здравствуйте, Oyster, Вы писали:
O>Спасибо за код. Я так понял, что Ruby — язык с динамической типизацией?
Да.
O>В общем-то, не нравится мне в его средствах метапрограммирования то, что они недалеко ушли от сишных макросов — фактически мы собираем код в текстовом виде. Со всеми вытекающими в виде отсутствия контроля типов в компайл-тайм (но в Ruby-то этого и так нет, если я всё правильно понимаю) и невнятных ошибок в случае, если что-то генерится неверно (на простом примере можно и глазками посмотреть, а вот на сложном...).
Уж не знаю... С сишными макросами ничего общего.
На счет невнятности сообщений об ошибках: попробовал сейчас сделать синтаксическую ошибку в методе generate, убрал закрывающую скобку в конструкторе. Получил такое сообщение:
t3.rb:37:in `object': (eval):7:in `object': compile error (SyntaxError)
(eval):6: syntax error from t3.rb:41:in `eval'
from t3.rb:37:in `object'
from t3.rb:41
(eval):7 -- это как раз место, где Ruby обнаружил синтаксическую ошибку (т.е. 7 -- это номер строки, которую обрабатывал eval). Если результат generate где-нибудь распечатать, то ошибка определяется достаточно точно.
O>Ну и ещё не нравится то, что нельзя оперировать кусками AST, т.к. это может быть очень полезно (ну там — пройтись по AST рекурсивно и сделать что-нибудь для нодов определённого типа). Или можно, но в коде это не использовалось?
Я вообще не представляю, какое у Ruby AST и зачем над ним чего-нибудь делать. Чем мне меньше всего хочется заниматься -- так это копаться во внутренностях компилятора.
По-моему, недавно на какой-то конференции по Ruby был доклад про какую-то штуку, которая Ruby код позволяет в виде дерева представлять. Но вот насколько это удобно --
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Уж не знаю... С сишными макросами ничего общего.
Я имею в виду принцип — сначала собираем код как текст, потом компилим (интерпретируем).
E>(eval):7 -- это как раз место, где Ruby обнаружил синтаксическую ошибку (т.е. 7 -- это номер строки, которую обрабатывал eval). Если результат generate где-нибудь распечатать, то ошибка определяется достаточно точно.
Да это-то понятно, что на ошибку в сгенерированном типе он укажет. Вот указал бы он на ошибку в макросе... но это просто невозможно при таком подходе.
Здравствуйте, Oyster, Вы писали:
O>Да это-то понятно, что на ошибку в сгенерированном типе он укажет. Вот указал бы он на ошибку в макросе... но это просто невозможно при таком подходе.
При таком подходе макросов, как таковых, нет.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Oyster, Вы писали:
O>Ну и ещё не нравится то, что нельзя оперировать кусками AST, т.к. это может быть очень полезно (ну там — пройтись по AST рекурсивно и сделать что-нибудь для нодов определённого типа). Или можно, но в коде это не использовалось?
Вот штука, которая должна позволять работать с Ruby AST: Parse Tree.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, VladD2, Вы писали:
VD>Куда же они делись? Самые что ни наесть, но текстуальные. Именно это и роднит их с С. И это их основаня проблема.
Влад, макросы в C/C++ -- это средство, модифицирующее исходный код программы до его обработки компилятором. В Ruby выполняется динамическая трансляция исходного текста во внутреннее представление с последующим исполнением получившегося кода (фактически интерпритация). В таком подходе нет отдельной фазы разворачивания макросов перед трансляцией. Более того, например, декларация класса в Ruby -- это тот же самый код, который исполняется последовательно, что позволяет, к примеру, делать так:
class SomeClass
if $debug_mode
# Такой конструктор будет у класса, если работаем в отладке.def initialize( param1, param2, param3 )
# ...end# Так же для отладки добавляется специальный метод.def debug_dump( debug_stream )
# ...end
else# В release у класса будет другой конструктор.def initialize( param )
# ...end
end
end
Фактически, исходный файл для Ruby-интерпритатора -- это исходный поток, из которого нужно извлекать очередную строку, транслировать ее во внутренее представление и исполнять. Поэтому совершенно нормальным является временная смена этого исходного потока на строку, сформированную самой программой.
Как следствие этого, в Ruby можно сделать то, что в принципе невозможно сделать макросами C/C++ -- изменить код программы после трансляции.
Так что, никакие это не макросы, это особенность интерпритации кода.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Влад, макросы в C/C++ -- это средство, модифицирующее исходный код программы до его обработки компилятором. В Ruby выполняется динамическая трансляция исходного текста во внутреннее представление с последующим исполнением получившегося кода (фактически интерпритация). В таком подходе нет отдельной фазы разворачивания макросов перед трансляцией.
Без разницы. Разница только в том на каком уровне работают макросы. На уровне текста, или на уровне АСТ.
E>Более того, например, декларация класса в Ruby -- это тот же самый код, который исполняется последовательно, что позволяет, к примеру, делать так: E>
E>class SomeClass
E> if $debug_mode
E> # Такой конструктор будет у класса, если работаем в отладке.
E> def initialize( param1, param2, param3 )
E> # ...
E> end
E> # Так же для отладки добавляется специальный метод.
E> def debug_dump( debug_stream )
E> # ...
E> end
E> else
E> # В release у класса будет другой конструктор.
E> def initialize( param )
E> # ...
E> end
E> end
E>end
E>
Ну, и в чем тут отличие от С? Замени if на #ifdef и ...
E>Фактически, исходный файл для Ruby-интерпритатора -- это исходный поток, из которого нужно извлекать очередную строку, транслировать ее во внутренее представление и исполнять. Поэтому совершенно нормальным является временная смена этого исходного потока на строку, сформированную самой программой.
Ключевое слово выделено. Формирование строк слишком подверженно ошибкам и имеет слишком слабый контроль. Плюс это полностью отрежает возможность получать информацию о типах.
Итерпретируемость язык конечно дает некоторую выгоду в данной ситуации, но положения дел не меняет.
E>Как следствие этого, в Ruby можно сделать то, что в принципе невозможно сделать макросами C/C++ -- изменить код программы после трансляции.
Это не заслука макроподсистемы. Это заслуга интерпретатора. В С++ моного нельзя того что можно в Руби. Но это уже ограничения подхода (статически типизированного языка).
E>Так что, никакие это не макросы, это особенность интерпритации кода.
То что ты показал — это макросы чистой воды. Причем худший их вариант.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Результат будет тот же самый.
> Ключевое слово выделено. Формирование строк слишком подверженно ошибкам > и имеет слишком слабый контроль.
О каких ошибках и о каком контроле ты говоришь?
> Плюс это полностью отрежает возможность > получать информацию о типах.
Не понимаю.
> E>Так что, никакие это не макросы, это особенность интерпритации кода. > > То что ты показал — это макросы чистой воды. Причем худший их вариант.
Чем же это худший вариант? Обычная кодогенерация в run-time.
Просто в динамических языках работа идет по другому. Поэтому не нужно пытаться повесить на динамические языки ярлыки из статически-типизированных языков.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E> В Ruby выполняется динамическая трансляция исходного текста во внутреннее представление с последующим исполнением получившегося кода (фактически интерпритация). В таком подходе нет отдельной фазы разворачивания макросов перед трансляцией. Более того, например, декларация класса в Ruby -- это тот же самый код, который исполняется последовательно, что позволяет, к примеру, делать так:
Похоже на препроцессор. Я так понимаю, что два раза с разными параметрами такой код не вызвать?
Видно, что на втором шаге появился метод release_mode.
Комментарий: в Ruby классы являются открытыми, что позволяет расширять класс новыми методами уже по ходу работы программы. Именно поэтому на втором шаге появился метод release_mode. Но ранее определенные в классе методы просто так не исчезают, т.к. они уже были определены. Поэтому на втором шаге метод debug_dump остался в списке методов. Чтобы изъять метод debug_mode нужно было бы переписать определение класса так:
class TestClass
if $debug
def debug_dump
end
remove_method :release_mode if method_defined? :release_mode
else
def release_mode
end
remove_method :debug_dump if method_defined? :debug_dump
end
def initialize
end
def hello_world
end
end