Pull to refresh

Comments 56

Еще одно подтверждение тезису, что делать тщательно и правильно нужно с первого раза (о переводе). Переводчикам — спасибо!
[ответ ниже, прискорбно промахнулся]
Моя вина, слишком уж хотелось поделиться с миром интересной находкой.
О коллективном переводе узнал уже после публикации.
язык интересный, но(!):
1) Я не понимаю что они там такого наворотили, что столько портировали на родственную ОС — FreeBSD;
2) Нету нормальной поддержки x86_64. Я с большим удовольствием наваял на D простой каркас MVC cgi фреймворка, но этот недостаток ставит на нем крест. Мне нужна поддержка x86_64 как минимум для Linux и FreeBSD.
К сожалению пока он не выглядит очень вкусным=(
+Tango, индустриального качества, бывало не собиралась на некоторых релизах.
Для Linux x64 есть ldc. FreeBSD — да, увы.
Впрочем, никто из коммьюнити/авторов особо и не скрывает — ещё пилить и пилить, человекоресурсов не хватает. В FAQ это довольно подробно отмечено.
A можно Хелло Ворлд?! А то искать неохота =)
[см. ниже, я сегодня умудрился промазать при ответе три раза (facepalm)]
Вообще-то, приведено в самом начале второй части статьи. Копирую исключительно в честь пиар-кампании ;)
import std.stdio;
void main()
{
  writeln( "Hello, world!");
}
Задаю вопросы.

Хочется, чтобы, например, вот такое:

> FILE f;
> fopen(f, «foo.txt», «rw»);
> fread(… );
> // забыли закрыть :)

или такое:

> unsigned char *ptr = malloc(512);
>… // какой-то код, использующий ptr
> free(ptr);
>… // еще код
> *ptr = 0; // попытка чтения по висячему указателю (dangling pointer)

не компилировалось. Такое возможно в D?
Первое решается и в С++, для этого создано ООП и RAII.

Второе невозможно отследить во время компиляции, ведь обращение к указателю может быть где угодно. Однако против таких ситуации тоже спасет ООП и RAII, если для хранения одиночных объектов в хипе использовать boost::shared_ptr, например, а вместо выделений массивов — std::vector.
Спасибо за коммент, но разговор идет именно о проверке этих инвариантов во время компиляции, а не об идиомах (RAII) и контейнерах, позволяющих обойти эти проблемы.
Нет, без макросов не решается при компиляции. По крайней мере я не нашёл способ. Анализ данных вроде зачаточный есть и некоторые обращения по нулевому указателю DMD ловит, но рассчитывать на это не нужно.

Если есть открытый ресурс, я обычно пишу в стиле LISP:

with_open_file("foo.txt", "rw", (File fd) {
    fd.read(...);
});


Вот реализация:
void with_open_file(string name, string access, void delegate(File file) dg) {
    fd = File(name, access);
    scope(exit) fd.close;
    dg(fd);
}
> with_open_file(«foo.txt», «rw», (File fd) {
> fd.read(...);
> });

Ну да, знакомо. :) А почему бы не использовать enum вместо «rw»/«r»/etc.?

А делегат где располагается? В куче? Можно ли выделить его в стеке?

В ATS такие штуки отслеживаются без накладных расходов в рантайме. Вот минимальный пример, функция opfile:

> fn opfile (path:string): void = let
> val (pfopt | fp) = fopen_err (path, file_mode_r)
> in
> if fp null then let
> prval Some_v (pf) = pfopt
> in
> fclose_exn (pf | fp)
> end else let
> prval None_v () = pfopt
> in
> prerrf («opfile(\»%s\"): failed to open\n", @(path))
> end
> end

Тут мы сначала открываем файл, потом проверяем (как и в Си) код возврата, потом, если получилось открыть его, сразу же закрываем, :) а если не получилось, то пишем в stderr сообщение об ошибке.

Надо бы попробовать такую штуку написать, как with_open_file.

PS: ага, выглядит жутковато.
В данном случае — на стеке. Для делегатов решение о выделении памяти под него на куче выполняется на основе (консервативного, не точного) escape анализа. т.е. если бы делегат получался из функции:
with_open_file("foo.txt", "rw", getProcessDelegate());

то объявление его в недрах getProcessDelegate произошло бы на куче.
Делегат, как я понимаю — просто указатель на кусок кода, вроде указателя на функцию в Си (+ указатель на кусок памяти с переменными для closure), т.е. 2 числа, так что не сказал бы, что он несет какие-то особые накладные расходы.

Перемнные, завернутые в closure выделяются на куче, да, а как иначе они могут пережить родную функцию?

по-моему, делегат еще обязательно должен быть объектно-ориентированным
Объектно-ориентированный делегат? :D
А в D все хитрее: там есть делегат- — анонимный метод объекта, а есть делегат — вложенная функция (т.е. находящаяся внутри другой функции), имеющая доступ к ее переменным даже после завершения той (замыкание).

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

И кстати, по моему это круто: компилируемый язык с делегатами и замыканиями, нет? При том, что ему не нужны многогмегабайтные тормозные сборки .NET или явы.
> Делегат, как я понимаю — просто указатель на кусок кода, вроде указателя на функцию в Си (+ указатель на кусок памяти с переменными для closure), т.е. 2 числа, так что не сказал бы, что он несет какие-то особые накладные расходы.

Угу, замыкание это указатель на функцию + указатель на окружение (которое, по сути просто struct какой-то).

Выделение всего этого дела в куче — вот накладные расходы (хотя во многих случаях этим можно пренебречь).

Вот тут описано, как в ATS память под замыкание можно выделить на стеке вызывающей функции:

www.ats-lang.org/TUTORIAL/contents/stack-allocation.html
Почему в ats все (почти) слова в идентификаторах и кейвордах сокращены до трёх букв? :D

clo, lam, arr, ptr.
Ну хранить переменные в стеке можно, если например, замыкание передается внутрь функции, там используется и отбрасывается (что кстати чаще всего и бывает).

По поводу оверхеда — думаю, небольшой. Никто (я надеюсь) не использует замыкания для часто вызваемых процедур к примеру, если их использовать для вещей типа with_open_file ( как в примере выше в комментах) — операции открытия файла намного медленнее выделения небольшого кусочка памяти.

А в ATS невменяемый синтаксис, уж извините. ни Лиспа, ни Си не люблю из-за этого ((
/offtopic{У Lisp'а прекрасный синтаксис на самом деле. Простой и понятный. С остальным согласен, к сожалению, но синтаксис — не главное}
Да, пример именно с with_open_file немного отвлекает от темы (уж там замыкание можно и выделить на куче :) — хотя, может быть, в embedded или каких-то особых ситуациях такое делать не рекомендуется).

На самом деле, хотелось показать то, чего нет в D:
— возможность записывать сложные пред- и постусловия прямо в коде (и давать компилятору проверить их выполнение до запуска программы)
— возможность сочетать эффективность и безопасность (для сравнения, RAII — это безопасность и выразительность)

В D типы обычно не используются для улучшения безопасности (по-моему, там основной фокус в увеличении выразительности и абстрагирования от некоторых деталей, что тоже дает безопасность: например, можно сравнить ссылки и указатели). В ATS же можно указать некоторые аннотации для стандартных функций POSIX и облегчить их безопасное использование (если эти функции удовлетворяют тем предпосылкам, которые даны в их типе, то все будет хорошо).
Ну по поводу проверки — в D есть неплохие штуки типа assert() (работает только в дебаг режиме), что-то с инвариантами классов, и что-то для фанатов юнит-тестов — по мне, не так уж мало.
Да, я даже пользовался этим. ^_^ Очень удобно, особенно когда модифицируешь код (хотя, надо признать, Contracts в PLT Scheme все же лучше).

Жаль, что в D весьма куцые возможности по проверке программ во время компиляции. :(
Разработчик и коммьюнити на ATS даже краем глаза не смотрели. Если я хоть немного умею читать незнакомые языки (:]), то в D можно реализовать пару фичей, не изменяя уже имеющиеся и так, что они не будут выглядеть прикрученными сбоку.

А пока есть только static assert, который можно использовать для проверки условий в разворачивании шаблонов или CTFE. Чего нет у C++. Например, функция sort, написанная Александреску, ругается, если в качестве предиката ей передать что-нибудь вроде «a = b».
Ага. Было бы интересно посмотреть, что из ATS можно приделать к D.
Например, реализовать индексированные монады, а с помощью них — легковесные зависимые типы (как у Киселева тут).
В реализацию стоит еще добавить try/catch, ну или вообще обработку ошибок)

И разве в D перед делегатом/анонимной функцией не пишут function/delegate() или как-то так?
> В реализацию стоит еще добавить try/catch, ну или вообще обработку ошибок)

Она есть. :) scope(exit) выполняется в любом случае. scope(failure) — при выходе из stack-frame'а по exception. scope(success) — при выходе по return. Если в пользовательском коде произойдёт ошибка, выполнится fd.close;

> И разве в D перед делегатом/анонимной функцией не пишут function/delegate() или как-то так?

Это сокращённая форма. Полностью будет так:
with_open_file("foo.txt", "rw", delegate void (File fd) {
    fd.read(...);
});


Но delegate компилятору не нужен. Синтаксис и без него получается контекстно-независимым. А возвращаемый тип определяется автоматически по возвращаемым значениям в делегате. Если их нет — значит void.
Точно. scope() я как-то не заметил! Но ведь try… catch ..finally, по моему проще для восприятия, нет?

А счинтаксис без слова delegate мне нравится :) Почти что руби получается )) да и реализация замыканий в D вроде не самая плохая.
try… catch… finally добавляет облась видимости там где он не нужен.

Сравните
auto fd = File(name, access);
scope(exit) fd.close;

и

File fd = null;
try {
    fd = File(name, access);
} finally {
    fd.close;
}
А каким вообще образом компилятор может проверить такие вещи? Если файл открыт в одной функции, а закрывается в другой. Или указатель освобождается в одной функции, но используется в других. Это можно увидеть только во время выполнения. А чтобы гарантировать что-то во время компиляции, нужно накладывать дополнительные ограничения на стиль использования ресурсов, то есть пользоваться идиомами.
> А каким вообще образом компилятор может проверить такие вещи?

Способы есть, они обычно основываются на соответствии между типами и логическими формулами (т.н. изоморфизм Карри-Говарда).

Иными словами, типы в программе — это и есть логические формулы! :) Ну а программы — это доказательства теорем, которые описаны типами.
Например, тип такой функции:

fun {a:type} identity(x:a):a = x

(то есть функция, которая для любого терма типа a возвращает его же: id(1) = 1, id(«foo») = «foo», etc.) в логике будет

forall a:type. a -> a

Как-то так.
Как уже сказали ниже, используйте RAII. Выдавать ошибку при компиляции можно, написав обёртку для fopen, fread. Если интересно — приведу пример. Но принято решать эти проблемы так:
fopen(f, "foo.txt", "rw");
scope(exit) close(f);
fread(...);


Второй можно решить так же, но лучше доверить сборщику мусора:
auto ptr = new char[512];
JFYI есть ещё отечественный D (http://okante.narod.ru/D/). Автор безумен чуть более, чем полностью. Чтение материалов по Д (коих множество) доставляет. Д не имеющий никакого отношени к D.
Как и следовало ожидать, всю карму получит тот из переводчиков, кто опубликует статью. FAQ — тоже коллективное усилие. Определённого единственного автора нет.
Не знаю как у вас, товарищи, а у меня dprogramming.ru уже неделю-другую как не открывается ни под каким видом. Если быть точным, DNS не находит хост.
Как я только что узнал, владелец домена проворонил его оплату со всеми вытекающими. Пока не знаю, чем это закончиться.
А вы не в курсе про портирование D на Native Client? Пробовал через гугл поискать на эту тему, но что-то все глухо… Просто я думаю гугл будет активно продвигать NaCl и у D в этом плане есть неплохой шанс выстрелить, т.к. С++ довольно сложен для тех кто привык к Java или C# и пока не понятно появятся ли они на NaCl (ибо там сложно всё с динамическим изменением кода), а вот у D есть возможность заработать на NaCl уже сейчас.
Насколько мне известно, ничего подобного не планируется. Можно, конечно, предложить это направление в официальном newsgroup.
Не думаю, впрочем, что это найдёт большой отклик, ибо:
а) Сейчас однозначным приоритетом ялвяется создание безбажного компилятора, реализующего спецификации и стандартных библиотек ( под обычные платформы )
б) «Заработать» на какой-то определённой нише противоречило бы позиционированию D как языка широкого применения, это задача для тех, кому нужен D в NaCl, но не авторов D.
Угу это уже есть — www.digitalmars.com/d/archives/digitalmars/D/pNaCl_107771.html

Просто это реальный шанс языку приобрести популярность, потому что на NaCl будет нужен более простой язык, чем C++, когда Actionscript, Java, C# программеры захотят программить для Chrome OS. И D просто идеальный вариант для этого. Впрочем, может портанут Java примерно как для Android, но в любом случае тут у всех будут равные шансы и у D их больше всего.
Я предвзят и не люблю NaCl как концепцию, так что воздержусь: )
Newsgroup покажет.
Хороший язык! Удобненький.
Orfo Switcher я писал именно на нем.
Но теперь приходится все переписывать назад опять на C++ :)
Теперь над программой работаю не я один, а мой напарник не знает этого языка, а C++ привычен нам обоим. Да и, если кто-то еще за проект возьмется, опять же ди — по-прежнему экзотика, возможны проблемы. Я даже исходники выкладывал программы — редкая душа бралась подправить их :)
C++ как-то по-популярнее все-таки
Не быстрее ли научить напарника D, чем переписать всё под С++? :)
А как у D ситуация с различными библиотеками, фреймворками, биндингами и прочим?
С нативными — средней паршивости, хотя стандартная библиотека ( phobos ) за последние месяцы набрала невероятный темп развития. Большинство открыто доступных родных библиотек\биндингов можно найти на dsource.org — главное не путать D1 и D2.

С другой стороны, возможно использование напрямую любой Cишной библиотеки, для этого необходимо лишь преобразовать сишные заголовочные файлы в D модули по формальным правилам и соответствующая библиотека будет слинкована с D кодом. Для windows есть официальная тулза, автоматизирующая этот процесс — www.digitalmars.com/d/2.0/htod.html; Для Linux я хочу написать аналогичную, но всё как-то некогда, да и руками достаточно быстро получается :)
Поддержки либ C++ значит как не было, так и нет?

Насколько возможно из D пользоваться либами Qt, wxWidgets, GTK?

Да и вообще насколько при разработке софта на D приходится писать велосипеды?
Отчасти есть, но очень ограниченная проблемами C++ ABI, подробнее здесь: www.digitalmars.com/d/2.0/cpp_interface.html. Насколько мне известно даже обсуждалось предложение выкинуть эту функциональность как малоиспользуемую и глючную :) Обычно просто делают оболочку к С++ либе с extern «C», и линкуют уже её.

Родные биндинги к Qt и GTK есть на dsource.org точно, wxWidgets — не знаю. Не могу судить об их качестве, т.к. ничего с GUI на D я не писал. Велосипеды писать чаще хочется, чем приходится — использование C библиотек покрывает большинство требований, но потратив некоторое время на велосипед, можно получить намного более изящный интерфейс :)

Если речь о production-ready — не знаю. Компилятор теперь, пожалуй, да. Библиотеки, скорее, всё же нет — сейчас как раз период активных изменений.
Не расскажете, о каких активных изменениях идёт речь?

И когда, по вашему, D более-менее устаканится и перестанет ломаться совместимость, так что можно будет с чистой совестью вкладывать в эту версию своё время, усилия и писать на ней код?
А то возникает соблазн ещё обождать, пока наконец не будет достигнута окончательная стабильность.
Не могу пошерстить newsgroup'ы с работы, когда окажусь дома — сделаю выборку интересных сообщений :)
DMD даже не может нормально скомпилировать: DMD1 — «не является приложением Win32» (такая же проблема и с DMC!), а файл, созданный DMD2, вызывает ошибку обращения к нулевому адресу, даже если нет кода — один main («больше не придётся часами искать ошибку»).

У меня сложилось впечатление, что это — урезанный вариант C++: сделали более строгим, добавили ограничений, загнали всё в классы, и так «изобрели гениальный язык взамен устаревшего Си»

Информация по языку отсутствует: в сети, включая сайт разработчиков, — одна «вода». Авторы Си — Керниган и Ритчи — когда-то написали отличную книжку, а эти только жалуются, что их язык не популярен…
Sign up to leave a comment.

Articles

Change theme settings