Неприятное в BlitzMax, ч.1 - язык

1.1. Отсутствие перегрузки функций. Это большое зло. Это надо, это сокращает время для написания кода. Плюс этот недостаток порождает целый ряд других.
Пример:
Нужен менеджер ресурсов. Там нужен такой метод LoadRes(target:TypeOfResource var, url$). Вот при перегрузке функций, нужная функция в зависимости от типа нужного ресурса будет подставляться на этапе сборки. А без перегрузки я должен: первое - писать свой обработчик по типу ресурса (если сделать одну функцию с параметром типа общего объекта - Object) - тратить свое время на лишний код! И второе - этот код будет выполняться во время работы процесса, что будет снижать быстродействие! Уже два ОГРОМЕННЫХ МИНУСА!
Можно обойти это немного, конкретизировав методы загрузки ( LoadImage(url$), LoadSound(url$) ), но это немного снизит удобство. Т.к. для каждого ресурса надо будет помнить свою функцию.

1.2. Отсутствие конструкторов с параметрами. Писать функции для создания классов - не очень то и удобно с точки зрения поддержки и развития кода. - Их ведь нельзя наследовать и п1 - нельзя перегружать. Делать для инициализации статические методы (тоже что и функции по сути) при наличии потомков - тоже неудачно и упирается в отсутствие перегрузки функций и методов - см.сл.п.

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

1.4. Невозможность создания указателей на типы пользователя. Например я сделал тип TRGBA_Color с полями R, G, В, и А соответсвенно для цветов и прозрачности. Сделать так я не могу:
Local p:TRGBA_Color Ptr = TRGBA_Color Ptr(bmp.PixelPtr(0,0))
Т.е. получить указатель на пикселы и работать с ним красиво - p.R = 100. Приходится делать это указателем на байты, что с ЯП высокого уровня не очень то и ассоциируется. Похоже, это связано с тем, что каждый тип является объектом и переменная этого типа уже и есть указатель на объект и заботой о защите памяти (только safe - функционал). Приведение типов или индексный доступ, конечно, не защищены от "дурака" - прогламмера (unsafe - функционал), но позволяют "не дураку" нормально и удобно работать.

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

1.6. Статический step в циклах. Иногда надо, чтобы при проходе по массиву приращение индекса менялось. Приходится делать через while/wend.

1.7. Нет оператора With. Можно сделать аналог, но это увеличивает громоздкость кода.

1.8. Система слежения за освободившейся памятью (менеджер памяти языка) и автоматическим удалением не нужных уже объектов, хоть и хороша и удобна, но не совершенна. И порой возникали утечки из-за перекрестных ссылок (кросслинков). Т.е. все равно приходится следить за корректной очисткой памяти, что отвлекает от основного процесса написания кода, увеличивает объем текста программы и в сумме эти два фактора приводят к увеличению времени написания программы. Плюс это не характерно для ЯП высокого уровня - менеджер памяти должен быть на высоте. Так что это получается что-то среднее между ЯП высокого и среднего уровней.
Утечки возникают даже не при кросс-линках а и в ситуациях, когда возникать не должны. Например: объект-контейнер, содержит в себе список других объектов. Объекты содержат поле с линком на элемент списка типа TLink, - не на сам контейнер. На этот контейнер ссылается глобальная переменная, поэтому он и "живет". Глюк: если в методе объекта из списка контейнера глобальная переменная переопределяется (в "пусто" или другой контейнер), то память не очищается! Контейнер не удаляется! При освобождении объекта в другом месте - все нормально и память освобождается. Глюк лечится, если объект из контейнера при переопределении глобальной переменной выбрасывается из списка - Tlink.Remove(). Но вообще-то такого быть не должно.

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

1.10. Нет нормальной полноценной совместимости (приведения) типов. Неоднократно сталкивался с тем, что надо было массив TObject[] преобразовать к моему типу (например для возврата из функции, для конвертирования из TList к массиву). Приходилось писать свои функции для преобразования вроде ObjArrayToTMytypeArray: TMytype[]( Object[] ). Можно в принципе и без этого - приводить типы уже во время работы с массивом, но наглядность и легкость чтения кода теряются. А это надо для реюзабельности кода.
П.с.: Таки сделали приведение типов для массивов - хорошо хоть развивается язык, а не стоит на месте.
П.п.с.: Млин. Криво работает. Нулевые массивы на выходе часто.

1.11. Нет директивы inline. Нужна она! И не только для ускорения работы кода.

1.12. Глюк с продолжением строки: если второй строкой идет комментарий, то выдается ошибка:
Field pms:TPixmap[] = [ ..
'LoadImage("images/gem_1_rotate.png") ..
- это нужно для комментирования при экспериментах с различными значениями в массиве.

1.13. Оптимизация кода очень плохая. Нет оптимизации по использованию методов - методы, которые ни разу не использовались в коде, все равно компилируются. Тот код, который пишется, буквально переводится один в один.

1.14. Нет инкапсуляции, т.е. защищенных и открытых методов и полей класса. Есть ее какое-то подобие на уровне модуля, но не в классе. А отсутствие инкапсуляции - это есть unsafe.

1.15. Нет автоматического приведения типов. Т.е. если в функции объявлен один тип, а передается другой, причем совместимый (общий класс Object), возникает ошибка.

1.16. Игры с типами не доделаны. Вот начал я привыкать к этим играм с типизацией и ощутил необходимость ссылки на тип. То, что в С++ шаблонами можно сделать. Такая переменная в которой нужный тип обозначен - очень нужно для наследования и для различных фабрик объектов, чтоб кода писать меньше. А то можно и приустать конструкторы под каждый новый тип переписывать, раз уж нет их с параметрами и перегрузки функций тоже нет.
П.с.: !!! Я придумал, как это обойти. В нужных нам классах метод Copy(), создающий копию класса. Неужели сделали перегрузку хоть на уровне преобразования возвращаемого результата с учетом наследования. Но - нужно самому писать весь код по копированию.

1.17. Нет автоматических конструкторов копирования объектов. Приходится писать самому - а это время. А время - деньги.

1.18. Несколько глупо реализовано конструирование объектов при наследовании. Переопределенный конструктор предка вызывается по любому, даже если мы его не вызываем и его вызов нам не нужен, т.к. мы переопределяем конструктор в наследнике. Практический пример: нужно НЕЗНАЧИТЕЛЬНОЕ изменение предка. Предок использует поле объекта одного типа, в наследнике нужно это же поле, но другого типа. В конструкторе этот объект создается. В наследнике значит надо переопределить создание этого объекта на нужный нам тип. Но в конструкторе предка этот объект тоже создается, а потом пересоздается в наследнике. Конечно, менеджер памяти лишний объект потом удалит, но вообще получается очень глупое и неразумное использование ресурсов. Хорошо если этот объект маленький, а если требует серьезных расчетов, подгрузки ресурсов и т.д. - получится заметное снижение производительности. Поэтому предок надо писать заново либо с нуля, либо с предка предка, еще не имеющего нужных нам полей объектов. Вывод: Язык не очень дружелюбен к наследованию и, следовательно, к хорошей поддержке крупных и долгосрочных проектов.

Комментариев нет: