Здравствуйте, AVC, Вы писали:
E>>Алексей, статическая/динамическая и строгая/слабая типизация -- это ортогональные понятия, как это хорошо объяснил _vovin вот здесь: Re[25]: ТипизацияАвтор: _vovin
Дата: 04.03.05
и здесь: Re[27]: ТипизацияАвтор: _vovin
Дата: 04.03.05
.
AVC>Евгений,
AVC>это очень интересная тема.
AVC>Насколько я вижу, ты всерьез увлекся возможностями динамических языков программирования.
Да, Алексей, тема очень интересная. Но я бы сказал, что это динамические языки (в частности Ruby) увлекли меня своими возможностями. Настолько, что вскоре наши с VladD2 споры будут идти не о C# vs C++, а о Ruby vs C#
А отвечать ниже я тебе буду как человек, который сам совсем недавно бывший ортодоксальным сторонником статической типизации. А теперь почувствовавший вкус динамической.
AVC>Мое возражение тебе (возможно, недостаточно хорошо продуманное) состоит в следующем: использование динамической типизации не требует отказа от статической типизации.
Чесно говоря, я вообще слабо себе представляю, как динамическая типизация может уживаться со статической. Разве что, как в C++ шаблонах. Но там мы не можем выйти за рамки того, что определили во время компиляции.
AVC>Такие языки, как Смолток, создаются во время горячего увлечения новой идеей.
AVC>Все старое и хорошо проверенное выкидывается как "старый хлам".
AVC>Как эксперимент (видимо, именно это Вирт называет academic exсersize) это очень интересно.
Возможно, в случае со Смолтолком так и было. Но тогда вообще была эра "Дикого Запада" -- языки с революционными идеями появлялись и имели возможность урвать для себя часть программистов. Нужно заметить, что Smalltalk-у и Lisp-у это вполне удалось. Поэтому они как-то здравствуют и поныне. Хотя их синтаксис, конечно, очень уж непревычен для меня. Возможно, синтаксис таких языков, как C, Pascal и производных от них так удобен именно потому, что их авторам не навился синтаксис Smalltalk/Lisp.
Однако, мир не стоит на месте. Тот же Ruby вобрал в себя очень много из того, что было в Smalltalk (фактически, все пункты, которые перечислил _vovin). Но при этом он имеет очень похожий на C/Perl/Python синтаксис и допускает программирование в стиле C/Perl, что очень облегчает вхождение в Ruby. Тем не менее, Ruby создавался не как реализация очередной академической идеи, а как прагматический язык для прагматических задач. Именно благодоря этому Ruby уже больше десяти лет, а его популярность в последние годы увеличивается. Так что, имхо, обкатаные в Smalltalk идеи были с успехом востребованны в более удобном (для меня), современном языке.
Дальше я попробую сравнивать твои и _vovin доводы с Ruby (т.к. Smalltalk я не знаю, да к тому же, "всяк кулик свое болото хвалит").
А вообще-то странно, ты сравниваешь Smalltalk и Oberon, а я C++ и Ruby
Но мы именно здесь не столько об Oberon vs Smalltalk, сколько о статическая типизация vs динамическая типизация.
AVC>Но давай посмотрим, а правда ли это дает такие преимущества?
AVC>AVC>В чем я вижу преимущества динамической типизации на примере
AVC>Lisp/Smalltalk (многие перечисленные преимущества являются следствием
AVC>предыдущих):
AVC> * Маленькое количество правил языка, простой, но очень гибкий и
AVC> выразительный синтаксис
AVC>Ну что же, Оберон вполне подходит. Только он не в пример читабельнее Смолтока.
Давай я объеденю этот пункт с еще одним:
AVC>AVC> * Более высокий уровень абстракции, благодаря чему можно на самом
AVC> языке можно создавать domain-specific languages
AVC>Интересно, в чем заключается "высота" абстракции в данном случае?
AVC>Я, например, использую в приложениях "специфические" АТД. Это ведь тоже абстракция?
Думаю, что в Ruby все же не такой простой синтаксис, как в Smalltalk или Pascal. Хотя бы потому, что это line-oriented язык. Т.е. в нем
def hello
puts "hello"
end
не может быть просто переписано в одну строку:
# Это не правильно!
def hello puts "hello" end
Нужно самому имитировать переводы строк:
def hello; puts "hello"; end
Так же можно припомнить, что скобки при вызове функций или объявлении Hash-ей можно опускать. Например, следующие записи эквивалентны:
def f(a); a.inspect; end
f :a => 3, :b => 4, :c => 5
f( :a => 3, :b => 4, :c => 5 )
f( { :a => 3, :b => 4, :c => 5 } )
В результате можно строить фактический собственные синтаксические конструкции средствами самого языка. Например, цикл loop в Ruby -- это на самом деле метод. А такая возможность определения собственных конструкций очень востребована при создании DSL-ей. Вот пара примеров.
c.field :name => :source_addr_ton,
:type => "oess_1::uchar_t",
:bit_mask => 0x1,
:default => 0
(взято из
Re: Использование метаданных в программах на языке C++Автор: eao197
Дата: 08.09.05
).
Этот код является описанием поля source_addr_ton с типом oess_1::uchar_t и начальным значением 0, для которого нужно сгенерировать порцию C++ кода. При этом я могу писать значения :name, :type, :bit_mask в произвольном порядке.
while line = gets.chop
if "quit" == line
break
elsif line =~ REQUIRE_REGEX
exception_guard do
require REQUIRE_REGEX.match( line )[ 1 ]
end
elsif line =~ CALL_METHOD_REGEX
exception_guard do
m = t.method( CALL_METHOD_REGEX.match( line )[ 1 ] )
m.call
end
elsif line =~ EVAL_REGEX
exception_guard do
eval EVAL_REGEX.match( line )[ 1 ]
end
end
print "=>"
end
(взято из
Re[14]: Следующий язык программированияАвтор: eao197
Дата: 04.10.05
).
Что такое exception_guard? Похоже на специальную синтаксическую конструкцию, вроде try/catch. На самом деле -- это штатный вызов обычной функции:
def exception_guard
begin
yield
rescue StandardError => x
puts x
end
end
Более подробно о возможностях Ruby для метапрограммирования и построения DSL см.
Re: Следующий язык программированияАвтор: eao197
Дата: 26.09.05
. В частности меня поразило, как в Ruby on Rails используют метапрограммирование для поддержки ORM:
class Project < ActiveRecord::Base
belongs_to :portfolio
has_one :project_manager
has_many :milestones
has_and_belongs_to_many :categories
end
Сомневаюсь, что на Oberon можно достигать подобного уровня абстракции.
AVC>AVC> * Нет необходимости писать много лишнего кода для многократного
AVC> указания типов
AVC>Здесь, конечно, Оберон "уступает" Смолтоку: он требует указания типа.
AVC>Но посмотрим с другой стороны: (1) указание типа повышает читабельность программы; (2) велика роль опечаток; (3) типы — не хлам, а строительные конструкции.
Здесь так же, имхо, нужно объеденить два пункта.
AVC>AVC> * Отсутствие статического контроля типов волей-не-волей *вынуждает*
AVC> задумываться о хорошем покрытии тестами. Как показывает практика, такое
AVC> покрытие столь же необходимо и для статических языков вроде Java. Но там
AVC> о нем задумываются позже, потому что создается ложная уверенность, что
AVC> компилятор позволяет избежать большинства ошибок
AVC>Необходимость хорошего тестирования бесспорна.
AVC>Но здесь выходит, что на стройке надо ходить без каски. Тогда, мол, строители будут осторожнее.
Во-первых, _vovin даже не делал намека на то, что типы -- это хлам. Важность типов в динамических языках ничуть не меньше, чем в статически-типизированных языках. Просто указание типов нужно делать гораздо меньше. Что не может не радовать. Вот есть у меня код на С++:
void f()
{
std::auto_ptr< Some_Class > my_object( new Some_Class() );
}
или же:
std::auto_ptr< Some_Class >
create_object()
{
return std::auto_ptr< Some_Class >( new Some_Implementation_Class() );
}
Аналогичные конструкции можно привести и в C#, и в Java, и в Oberon (поправь меня, если по поводу Oberon я не прав).
А как бы это выглядело на динамически типизированном языке?
def f
my_object = Some_Class.new
end
def create_object
return Some_Implementation_Class.new
end
Поначалу мне так же казалось, что запись на статически типизированном языке понятнее
Теперь я так не думаю.
Но самое удивительное открытие для меня было в том, что хоть переменные могут в динамике менять свой тип (скажем my_object может затем стать и Array, и Hash, и String), но проблем это не вызывает. Потому что, как оказалось, работа с такими "нетипизированными" переменными происходит в локальных контекстах (малых по объему методах), поэтому легко запомнить или увидеть тип каждой из них. А малый объем методов как раз достигается выразительной мощью языка и высоким уровнем абстракции.
Ну а на случаи ошибок действительно есть тесты. Тестов должно быть гораздо больше, чем для программы на статически типизированном языке. Это как раз главная причина, из-за которой я все еще мало программирую на Ruby. Но, если смотреть на это с другой стороны, тестирование не бывает избыточным. Если благодоря динамической типизации мы имеем возможность писать код быстрее, то у нас появляется возможность написать больше тестов. А увеличение количества тестов только увеличивает качество программы. Вот такой парадокс -- в Ruby запуская программу на выполнения я даже не уверен, что в ней нет синтаксических ошибок. Из-за этого я вынужден так гонять ее и в хвост, и в гриву на тестах, что начинаю быть уверенным в ее работоспособности даже больше, чем в случае с C++.
И еще одна штука о тестировании. Я еще не проверял ее на практике, но кажется, что так и есть. В C++ мне часто приходиться отлаживать объекты, которые очень сложно представить отдельно от остальных частей системы. Эти объекты встраиваются в сложный фреймворк, попают в нужное мне состояние по сложной траектории (через некоторую цепочку действий) и затем должна выпасть определенная комбинация условий, чтобы проверить работоспособность объекта. Для чего приходится создавать не менее сложные имитационные стенды. А все из-за того, что объект очень жестко связан со своим окружением. В динамически типизированных языках, где возможно использование
Duck TypingАвтор: eao197
Дата: 15.09.05
, как мне кажется, гораздо легче поместить тестируемый объект в искуственное окружение (sandbox) и в этом окружении дергать его как хочешь.
AVC>AVC> * Широкии возможности интроспекции/рефлекшна
AVC>Есть в Обероне.
Интересно, а можно пример? Вот в Ruby можно так:
irb(main):014:0> a = {}
=> {}
irb(main):015:0> a.class.name
=> "Hash"
irb(main):016:0> a.instance_variables
=> []
irb(main):017:0> a.class.instance_methods
=> ["reject", "[]=", "send", "object_id", "value?", "size", "singleton_methods", "to_hash", "__send__",
"member?", "equal?", "taint", "find", "frozen?", "instance_variable_get", "each_with_index",
"each_pair", "kind_of?", "delete_if", "merge!", "instance_eval", "require", "to_a", "replace",
"collect", "merge", "all?", "entries", "type", "store", "protected_methods", "extend", "detect", "eql?",
"values", "zip", "instance_variable_set", "hash", "is_a?", "empty?", "default", "map", "to_s", "any?",
"class", "sort", "tainted?", "private_methods", "default=", "default_proc", "key?", "min", "f",
"require_gem_with_options", "untaint", "find_all", "keys", "reject!", "invert", "each", "id",
"has_key?", "inject", "inspect", "delete", "==", "indexes", "===", "sort_by", "clone", "public_methods",
"fetch", "each_value", "max", "values_at", "respond_to?", "display", "index", "select", "freeze",
"shift", "update", "clear", "length", "has_value?", "__id__", "rehash", "partition", "=~", "methods",
"require_gem", "method", "indices", "nil?", "grep", "dup", "each_key", "instance_variables", "include?",
"[]", "instance_of?"]
Затем я могу по имени метода получить объект типа Method и вызвать его (пример показан выше, внутри одного из блоков exception_guard).
Про closures, continuations и green threads ничего не скажу, т.к. сам не владею терминологией Smalltalk-а.
AVC>AVC> * Хорошие инструменты для работы с кодом, браузеры, интерактивные
AVC> отладчики, возможность писать любой код во время отладки программы,
AVC> запускать отдельные участки кода практически из любого места среды
AVC> * Наличие среды разработки, написанной на самом языке с доступом ко
AVC> всем исходным текстам. Это позволяет на ходу подстраивать среду под себя
AVC> и на ее примере изучать строение серьезных программ.
AVC>Все это есть (и было с самого начала) в Обероне.
AVC>Сохранилось и в Компонентном Паскале.
AVC>При наличии статической типизации.
А вот есть ли в Oberon возможность менять типы прямо по ходу выполнения программы? Посмотри на пример, который я приводил в
Re[14]: Следующий язык программированияАвтор: eao197
Дата: 04.10.05
. Это ведь простейший случай, когда программа модифицируется прямо по ходу работы. Возможно ли такое в Oberon? В C++ точно не возможно.
AVC>Главное — все хорошо: и статическая типизация, и динамическая типизация, и использование утверждений (программирование по контракту).
AVC>Все это есть в Обероне.
А можно примеры того, что ты считаешь проявлениями динамической типизации в Oberon?
AVC>А вот в Смолтоке меня насильно лишают статической типизации, которая является для меня важным рабочим инструментом.
Вообще-то да, переход от статической типизации к динамической не прост. Первое время преследует чувство полной незащищенности, кажется, что капканы разбросанны на каждом шагу. Но оказывается, что это не так.
Disclamer: Алексей, не нужно думать, что я стал адептом динамически типизированных языков. Основную работу я все равно выполняю на C++. Но сейчас мне кажется, что лучше всего сочетать в работе оба подхода. Хотя бы на уровне использования в проекте двух языков. А то и написание основного каркаса на динамически типизированном языке, а наиболее ответственные/ресурсоемкие части -- на компилируемом статически типизированном.
Ну и напоследок анекдот, который, имхо, очень в тему:
Разговаривают католический священник и иудейский раввин.
Священник спрашивает:
-- Вот вам нельзя свинину есть, так неужели вы никогда ее не пробовали?
-- Ну был в молодости такой грех, пробовал. А вот вы давали обед безбрачия, неужели вы никогда не были с женщиной?
-- Ну был в молодости такой грех, пробовал.
-- Так вы мне и скажите, что лучше?
... << RSDN@Home 1.1.4 stable rev. 510>>
Здравствуйте, eao197, Вы писали:
Евгений,
с интересом читал твой пост.
Конечно, мне нужно его обдумать. Когда человек с большим опытом увлекается какой-то идеей, это "ж-ж-ж" неспроста ((c) Винни-Пух
). Значит, здесь есть что-то действительно ценное.
Все же попытаюсь сразу ответить на
некоторые реплики, зная, что мое "раздумье" иногда затягивается на много дней.
AVC>>Мое возражение тебе (возможно, недостаточно хорошо продуманное) состоит в следующем: использование динамической типизации не требует отказа от статической типизации.
E>Чесно говоря, я вообще слабо себе представляю, как динамическая типизация может уживаться со статической. Разве что, как в C++ шаблонах. Но там мы не можем выйти за рамки того, что определили во время компиляции.
ИМХО, если бы динамическая типизация не могла ужиться со статической, то мы не имели бы статически-типизированных ОО-языков.
Собственно, в литературе полно подобных утверждений: "Переменная x имеет
статический тип T и
динамический тип T1."
ИМХО, главные удобства динамически-типизированных языков основаны не на отказе от статической типизации, а на поддержке run-time system (RTS). Не зря же большинство из них (по крайней мере, в начале своего пути) —
интерпретируемые языки.
Интерпретатор и выполняет роль RTS.
Пока статически-типизированные компилируемые языки были "голыми", без run-time поддержки, как Си и Паскаль, динамически-типизированные языки имели огромное преимущество в гибкости.
Но как только статически-типизированные языки стали опираться на помощь продуманной небольшой RTS (как Оберон, например), это преимущество стало значительно меньше, в то время как помощь статической системы типов в
профилактике ошибок переоценить трудно.
E>И еще одна штука о тестировании. Я еще не проверял ее на практике, но кажется, что так и есть. В C++ мне часто приходиться отлаживать объекты, которые очень сложно представить отдельно от остальных частей системы. Эти объекты встраиваются в сложный фреймворк, попают в нужное мне состояние по сложной траектории (через некоторую цепочку действий) и затем должна выпасть определенная комбинация условий, чтобы проверить работоспособность объекта. Для чего приходится создавать не менее сложные имитационные стенды. А все из-за того, что объект очень жестко связан со своим окружением. В динамически типизированных языках, где возможно использование Duck TypingАвтор: eao197
Дата: 15.09.05
, как мне кажется, гораздо легче поместить тестируемый объект в искуственное окружение (sandbox) и в этом окружении дергать его как хочешь.
Вот я приводил подобные примеры, когда говорил об удобстве написания и отладки программы в обероновской среде.
А меня только ругали.
А основана эта фича в основном на
динамической загрузке модулей "по запросу". (Но модуль — не тип.)
AVC>>А вот в Смолтоке меня насильно лишают статической типизации, которая является для меня важным рабочим инструментом.
E>Вообще-то да, переход от статической типизации к динамической не прост. Первое время преследует чувство полной незащищенности, кажется, что капканы разбросанны на каждом шагу. Но оказывается, что это не так.
Вот этот бы аргумент Павлу Кузнецову!
Он утверждал, что использование обобщенного контейнера без шаблонов обязательно приведет к долгой и мучительной отладке.
А я этого что-то не замечаю...
E>Disclamer: Алексей, не нужно думать, что я стал адептом динамически типизированных языков. Основную работу я все равно выполняю на C++. Но сейчас мне кажется, что лучше всего сочетать в работе оба подхода. Хотя бы на уровне использования в проекте двух языков. А то и написание основного каркаса на динамически типизированном языке, а наиболее ответственные/ресурсоемкие части -- на компилируемом статически типизированном.
Да, иногда использовать два (и более?) языка удобно.
Не всегда это статический и динамический языки.
Вот простой пример: когда пишется компилятор Си, то часто совместно используются Си и yacc (BNF), потому что это
удобно.
E>Ну и напоследок анекдот, который, имхо, очень в тему:
E>E>Разговаривают католический священник и иудейский раввин.
E>Священник спрашивает:
E>-- Вот вам нельзя свинину есть, так неужели вы никогда ее не пробовали?
E>-- Ну был в молодости такой грех, пробовал. А вот вы давали обед безбрачия, неужели вы никогда не были с женщиной?
E>-- Ну был в молодости такой грех, пробовал.
E>-- Так вы мне и скажите, что лучше?
E>)
Я до сих пор вспоминаю анекдот про воробья и орла ("куда мы летим?").