Entry tags:
Language with a Bright future
В начале мая в логове фейсбука прошла конференция DConf, посвященная языку D. Финансировали ее всем миром через проект на кикстартере, я там тоже поучаствовал скромным спонсором. Видео всех выступлений были выложены на ютюбе, вот наиболее мне понравившиеся, которые могу порекомендовать всем тем, кто открыт к новому и имеет немного любопытства:
1. Web Development in D - какие есть замечательные инструменты (в первую очередь vibe.d), как с ними все получается просто и красиво (и эффективно), благодаря грамотному использованию возможностей D.
2. Writing Testable Code in D - тут больше демонстрация умений и удобств языка.
3. Metaprogramming in the Real World - занятный experience report от компании с сотней программистов, где весь код пишется на D.
1. Web Development in D - какие есть замечательные инструменты (в первую очередь vibe.d), как с ними все получается просто и красиво (и эффективно), благодаря грамотному использованию возможностей D.
2. Writing Testable Code in D - тут больше демонстрация умений и удобств языка.
3. Metaprogramming in the Real World - занятный experience report от компании с сотней программистов, где весь код пишется на D.
no subject
no subject
no subject
Rust выглядит наиболее обещающим и перспективным, но он сейчас наиболее недоделанный, все время там все ломают и переделывают.
D находится где-то между ними: как язык довольно приятен, реализации более-менее юзабельные, но есть еще недоделки.
Из этих трех мой выбор сейчас - D.
no subject
no subject
no subject
long fac(long n) { return iota(1,n+1).reduce!"a*b"; }
no subject
no subject
no subject
no subject
С вызовом функций в компайл-тайме на практике редко сталкиваешься, т.к. все интересные данные программа получает уже в рантайме, а при компиляции какие данные есть? Собственные исходники разве что, так что фича эта в основном используется внутри всяких шаблонов, генерирующих код. Чтобы код удобно было генерить, нужна работа со строками, массивами и основные алгоритмические штуки из стандартной библиотеки, вот они и используются. Т.е. в целом фича, может, и мегакрутая, но замечают это лишь библиотекописатели, а при написании обычных программ ее и не замечаешь, даже если где-то внутри используемых библиотек она и работает активно.
no subject
no subject
class MyClass(T) { ... static if (SomePredicate!(T)) { Result!T my_function(T x, ...) { .... } } ...Тут и квазицитаты не нужны, пишешь обычный код.
no subject
no subject
no subject
Типичные же юзкейсы - это получить на вход некоторый заранее неизвестный тип Т, используя развитую компайл-тайм рефлексию разузнать, подходит ли он и что он умеет, и на выходе родить новый тип R, сохраняющий или преобразующий полезные свойства Т и делающий что-то свое. Например, так комбинаторы map/take/reverse/zip и т.п. сохраняют такие свойства переданных в них последовательностей как "известная длина", "клонируемость", "доступ по индексу за О(1)" и т.д. Или, например, берется на вход некоторый класс и возвращается его аналог, где методы превратились в RPC вызовы или наоборт обработчики RESTful запросов. Вся рефлексия и рождение кода происходят при компиляции, рантайм-оверхед нулевой.
Ну и всевозможная сериализация-десериализация, конечно.
no subject
no subject
no subject
no subject
А если про конкретную реализацию, то:
1. Порой хочется увидеть результат раскрытия всех шаблонов. Вон в Си можно посмотреть результат препроцессора, в GHC куча всяких промежуточных представлений доступна, а тут я не знаю, как заглянуть в середину между исходником и бинарником. Воможно, в GDC или LDC что-то такое есть, не в курсе.
2. Сообщения об ошибках порой не шибко информативные. Например, если есть несколько реализаций шаблона с разными предусловиями, и ни один предикат не сработал, то шаблон не инстанциируется, при этом сообщение об ошибке может быть весьма неожиданным, приходится догадываться.
3. Если писать сложный код в ФП-стиле, где замыкание внутри замыкания замыканием погоняет, и все передаются как alias параметры во всякие комбинаторы, компилятору порой сносит крышу.
no subject
no subject
no subject
Что-то типа:
map(List(1, 2), x => x) возвращает List[Int]
map(Vector(1, 2), x => x.toString) возвращает Vector[String]
map(Map(42 -> "42"), x => true) возвращает BitVector
no subject
http://ddili.org/ders/d.en/ranges.html
Сейчас контейнеры умеют представлять себя в виде рэнждей, с которыми работают всевозможные алгоритмы и комбинаторы. Если контейнерам еще добавить универсальный способ создаваться из рэнджей (не помню, есть ли это сейчас), то запрошенная тобой функция будет совсем простой. Завтра могу попробовать сделать пример.
no subject
no subject
no subject
http://en.wikipedia.org/wiki/Expression_templates
т.е. сделать лямбды не простыми, а строящими AST. С перегрузкой операторов это не так сложно.
no subject
no subject
В целом, мне нравится дишный подход к метапрограммированию. Да, многие вещи довольно адхочно сделаны, но в целом все очень задорно, и, что главное, народ не стесняется МП как у нас в коммьюнити.
Конечно, когда я читал посты и тексты, в голове сразу начал работать сравнитель. С одной стороны, стратегия как в лиспах, как у нас, когда вычисления времени компиляции спрятаны за макросами, которые основаны на reflection API. С другой стороны ди, в котором есть куча разных средств CTM (как утверждает один пост, "here is the basic tool box: typeof, is, alias, static if, __traits, std.traits, CTFE."), которые можно юзать вперемешку с кодом.
Какой из этих двух стилей МП тебе больше нравится? Да, в ди некоторые вещи можно делать очень просто, но за это приходится расплачиваться пачкой фич в языке + винегретом из метауровней. Насколько это является проблемой на практике?
no subject
no subject
no subject
no subject
А я-то, грешным делом думал, что он сдох давно.
no subject
no subject
Например, вот в этом случае могут ли раскрываемые шаблоны видеть мемберы класса C и какие именно мемберы они увидят:
class C { int x; mixin(genJsonSerializable!C); // перебирает мемберы и создает метод toJson mixin(genXmlSerializable!C); // перебирает мемберы и создает метод toXml int y; }no subject
mixin template ToJSON(C) { void toJSON() { enum mbs = [__traits(allMembers, C)]; // compile-time created array writeln(mbs); } } mixin template ToXML(C) { void toXML() { enum mbs = [__traits(allMembers, C)]; writeln(mbs); } } class X { int x; mixin ToJSON!X; mixin ToXML!X; int y; } void main(string[] argv) { auto x = new X; x.toJSON; x.toXML; }Выводит:
["x", "toJSON", "toXML", "y", "toString", "toHash", "opCmp", "opEquals", "Monitor", "factory"]
["x", "toJSON", "toXML", "y", "toString", "toHash", "opCmp", "opEquals", "Monitor", "factory"]
no subject
no subject
no subject
no subject
module mymodule;
и в коде пишем:
writeln([__traits(allMembers, mymodule)]);
получаем
["object", "std", "ToJSON", "ToXML", "X", "main", "D12TypeInfo_Axa6__initZ"]
Если еще вспомнить про user-defined attributes, получаем довольно могучий струмент.
no subject
no subject
mixin template ToXML() { void toXML() { writeln([__traits(allMembers, __traits(parent, toXML))]); } } class X { int x; mixin ToXML; }Вызов toXML выводит
["x", "toXML", "toString", "toHash", "opCmp", "opEquals", "Monitor", "factory"]