LINUX.ORG.RU

Facebook платит за устранение багов в реализации языка программирования D

 ,


1

5

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

Одно из определений языка D: «D — это то, чем должен был быть С++». Вокруг языка сломалось уже много копий, но несмотря на это язык продолжает жить и развиваться, демонстрируя свои замечательные возможности и расширяя свое сообщество. Все больше разработчиков из мира С++/Java пристально следят за развитием языка и стараются держать руку на пульсе. Должен отметить, что сообщество D не является ортодоксальным и фундаменталистким (что бы это ни значило), и нередко в ньюсгруппах можно увидеть, что в ответ на вопрос, можно ли использовать D для решения определенной задачи, члены сообщества рекомендуют задавшему вопрос использовать другой язык, отличный от D. Так что в лице сообщества D любой найдет грамотных специалистов своего дела, готовых ответить на нужный вопрос кратко и по существу. Все это делает развитие языка неизбежным и неотвратимым.

Список багов с ценами за их устранение

>>> Оригинал новости

★★

Проверено: Shaman007 ()
Последнее исправление: cetjs2 (всего исправлений: 6)
Ответ на: комментарий от yetanother

Ну и если вы не хотите разобраться в концепции immutable данных, несмотря на то, что их пользуете аж в трех(или больше, не помню) языках, в том числе и в том, в котором их нет - это не значит, что эта концепция не нужна другим.

Этой концепции не может не быть. Если вы делаете свои данные неизменными, то вы используете эту концепцию. Делать неизменные данные вы можете в любом языке. Ваше понимание искажено терминологией D.

forCe
()
Ответ на: комментарий от yetanother

то я бы просто сделал массив логгеров и в сообщении бы хранил немутабельный индекс на мутабельный логгер. И все.

Ну вы сами непредвзято посмотрите на это со стороны. Могло бы быть так:

class LoggerChangedMsg {
  ...
  public Logger logger() { return logger_; }
  private Logger logger_;
}
class SomeWorkingThread {
  public void onLoggerChange( immutable LoggerChangedMsg msg )
  {
    logger_ = msg.logger();
  }
  ...
}
// Где-то в какой-то нити...
send( new LoggerChangedMsg( createLogger() ) );
Вместо этого предлагается:
class LoggerChangedMsg {
  ...
  public uint loggerIndex() { return loggerIndex_; }
  private uint loggerIndex_;
}
Logger[] loggers;
class SomeWorkingThread {
  public void onLoggerChange( immutable LoggerChangedMsg msg )
  {
    logger_ = loggers[ msg.loggerIndex() ];
  }
  ...
}
// Где-то в какой-то нити...
loggers[???] = createLogger();
send( new LoggerChangedMsg( ??? ) );

И это код, который нужно будет писать на современном ЯП, претендующим на что-то от функциональщины?

Да, можно сказать, что это добавит лишний уровень, но по крайней мере для логгера это точно не критично.

А вот не нужно за меня решать, что критично, а что нет. Язык должен предоставить набор удобных инструментов, а дальше я сам.

eao197 ★★★★★
()
Ответ на: комментарий от forCe

Этой концепции не может не быть. Если вы делаете свои данные неизменными, то вы используете эту концепцию. Делать неизменные данные вы можете в любом языке. Ваше понимание искажено терминологией D.

Вопрос здесь именно в транзитивности. Потому что если у меня есть, скажем, массив массивов (двумерный массив) и я объявлю его при создании immutable, то в D я получу неизменяемость всех элементов подмассивов. Но если immutable не транзитивно, тогда неизменяемыми будут только сами вложенные одномерные массивы, а элементы уже можно будет менять.

Представим теперь, что у нас есть навороченная структура, содержащая всякие многомерные массивы, структуры, классы, кортежи и прочие безобразия. Тогда объявление immutable этой структуры при отсутствии транзитивности почти ничего не даёт, поскольку можно будет переделать многие её глубоко вложенные элементы. Напротив, при транзитивной immutable вся структура оказывается неизменяема со всеми своими элементами и подэлементами.

Теперь представим, что кто-то принял вашу структуру и в своём потоке решил взять из неё массив и поменять его элементы. При транзитивной немутабельности он не сможет даже скомпилировать свой код. При нетранзитивной --- не только скомпилирует, но и ошибку времени исполнения не всегда получит, просто данные в какой-то момент изменятся, но никто кроме него об этом не узнает. Что чревато получением неверного результата где-то ещё в отдалённом будущем.

Vudod ★★★★★
()
Последнее исправление: Vudod (всего исправлений: 1)
Ответ на: комментарий от yetanother

Да, очередной язык, который «знает как лучше», а в результате приходится подпрыгивать при решении задач, которые решаются на си и с++.

forCe
()
Ответ на: комментарий от Vudod

Это вполне можно сделать на уровне используемых типов. Скажем, константный std::vector не дает менять свои элементы.

forCe
()
Ответ на: комментарий от forCe

в результате приходится подпрыгивать при решении задач, которые решаются на си и с++.

Реши задачу создания гарантированно иммутабельной структуры на Си.

tailgunner ★★★★★
()
Ответ на: комментарий от forCe

Это вполне можно сделать на уровне используемых типов. Скажем, константный std::vector не дает менять свои элементы.

Вообще-то говоря, это не совсем так.

В C++ такой фокус проходит, поскольку есть только const, а это всего лишь read-only view, не ограничивающий владельца объекта. Что и позволяет делать так:

class MyData {
  private :
    std::unique_ptr< SomeObject > obj_;
  public :
    MyData() : obj_( new SomeObject() ) {}

    const SomeObject & get() const { return *obj_; }

    void modify() { obj_.reset( new SomeObject() ); }
};

MyData d;
auto a = d.get();
d.modify();
auto b = d.get();
// При обращении к a отгребаем проблем.

Когда в языке появляется иммутабельность, то нельзя просто навешивать на объекты разные модификаторы. Т.е. вот так не получится сделать:

class MyData {
  private :
    std::unique_ptr< SomeObject > obj_;
  public :
    MyData() : obj_( new SomeObject() ) {}

    const SomeObject & get() const { return *obj_; }
    immutable SomeObject & get() immutable { return *obj_; }

    void modify() { obj_.reset( new SomeObject() ); }
};
Поскольку в obj_ хранится мутабельная ссылка на SomeObject.

Это дело можно было бы преодолеть, если бы объект MyData сразу бы объявлял obj_ как иммутабельную ссылку. Но тогда пришлось бы, в ряде случаев, иметь два разных типа MyData: один для использования в качестве иммутабельного объекта (obj_ иммутабельный, метода modify нет в принципе). Второй для использования в качестве мутабельного объекта (obj_ мутабельный, есть modify, нет иммутабельного get).

Но это лишний геморрой для разработчика.

По крайней мере причины появления транзитивности для const и immutable я себе вижу так.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ну я же сделал дисклеймер, что не претендую на идеальность решения. Я вам просто показал возможное решение. Во вторых оно отличается от вашего вариант только тем, что вам нужно будет создавать логгеры фабрикой. Причем сама фабрика будет shared и одна на все потоки, а выдавать будет immutable логгеры. Более чем чистое решение, чище того что вы показали - потому что массив спрячется в фабрике. Ну и на самом деле в отрыве от задачи мне сложно дать истинно правильное решение.

class LoggerChangedMsg {
  ...
  public uint loggerIndex() { return loggerIndex_; }
  private uint loggerIndex_;
}
class Logger{
    ...
    static Logger[] loggers_;
    static auto createLogger(...){
        loggers_ ~= new Logger(...);
        return loggers_.length-1;
    }
}
class SomeWorkingThread {
  public void onLoggerChange( immutable LoggerChangedMsg msg )
  {
    logger_ = loggers[ msg.loggerIndex() ];
  }
  ...
}
// C++
// send( new LoggerChangedMsg( createLogger() ) );
// D
send( new LoggerChangedMsg( Logger.createLogger() ) );
Разница только в создании логгера. Хотя тут вы мне наглядно показали, что что-то типа weak ptr было бы удобно в данной ситуации. Но это в конкретной ситуации. И тогда вам придется писать обработку ситуации что мутабельный пойнтер испортился - так что вариант с транзитивностью более правилен в своей строгости. А взять на себя лишний раз ответственность вы и так всегда можете с помощью cast(). Вот что мне в моем решении не нравится, что хранение индексов исключает из работы сборщик мусора, но при использовании кастомных аллокаторов это уже не имеет значения.

А вот не нужно за меня решать, что критично, а что нет. Язык должен предоставить набор удобных инструментов, а дальше я сам.

Так язык вам их и предоставляет. Если вы не хотите использовать immutable потому лишняя операция индексации для вас неприемлиема, вы всегда можете писать в императивном стиле с синхронизацией - этого у вас никто не отнимал. И опять же скажу, что я не претендую на автора самого правильного решения озвученного вами кейса.

yetanother ★★
() автор топика
Ответ на: комментарий от forCe

Это вполне можно сделать на уровне используемых типов. Скажем, константный std::vector не дает менять свои элементы.

Это не очень хорошее решение, потому что вам придётся делать это для всех типов, вт том числе и самописных. И делать это придётся очень сложным путём. Представьте себе объект, содержащий массив из объектов, каждый из которых содержит, например, словарь. Оба класса, к которым принадлежат объекты, написаны вами. Тогда вам придётся обеспечивать эту транзитивность самому. Более того, всякому, кто напишет унаследованный класс, придётся делать это же для новых полей.

Vudod ★★★★★
()
Ответ на: комментарий от Vudod

Как правило, иммутабельность используется для построения иммутабельных структур данных(т.е. такие структуры изначально проектируются как неизменяемые), а не для навешивания иммутабельности каким-либо левым типам(и я думаю, что для этого вполне достаточно const).

forCe
()
Ответ на: комментарий от forCe

Как правило, иммутабельность используется для построения иммутабельных структур данных(т.е. такие структуры изначально проектируются как неизменяемые), а не для навешивания иммутабельности каким-либо левым типам(и я думаю, что для этого вполне достаточно const).

Это странное мнение, потому что очень часто самые обычные структуры бывает полезно передать куда-то, чтобы там (например, на клиентской стороне) их использовали. Если же их нельзя сделать целиком immutable, то это опасно, потому что там их попортят. Если нужно будет их для этого пилить или делать специальные структуры, это тоже очень неудобно. Хотя, конечно, ко всему можно привыкнуть, в том числе к необходимости передавать длину массива и руками чистить память.

Vudod ★★★★★
()
Ответ на: комментарий от Vudod

Тогда вам придётся обеспечивать эту транзитивность самому. Более того, всякому, кто напишет унаследованный класс, придётся делать это же для новых полей.

В C++ что-то подобное делается посредством оформления (если это так уж необходимо) getter-ов в виде пары из константного и неконстантного методов. Хотя во многих случаях, getter-ы делаются просто константными.

AFAIK, в D приходится делать тоже самое, если объект хочет быть доступным как по обычной, так и по const ссылке.

Хотя для простых данных, которые не содержат getter-ов/setter-ов D-шая транзитивная константность, конечно, количество писанины уменьшает.

eao197 ★★★★★
()
Ответ на: комментарий от Vudod

Это странное мнение

Основано на опыте. Может что-то не так с опытом.

Это странное мнение, потому что очень часто самые обычные структуры бывает полезно передать куда-то, чтобы там (например, на клиентской стороне) их использовали. Если же их нельзя сделать целиком immutable, то это опасно, потому что там их попортят.

Для этого вполне достаточно использовать const-ссылки. Если разработчик класса не идиот, то при использовании const-интерфейса вы ничего не сможете испортить.

forCe
()
Ответ на: комментарий от forCe

Для этого вполне достаточно использовать const-ссылки. Если разработчик класса не идиот, то при использовании const-интерфейса вы ничего не сможете испортить.

Не сможете испортить в случае, если либо const тоже транзитивен средствами языка, либо если разработчик предусмотрел это при конструировании класса.

Vudod ★★★★★
()
Ответ на: комментарий от yetanother

Разница только в создании логгера.

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

Уж лучше в сообщении передавать лямбду, если это возможно.

Хотя еще лучше была бы возможность писать так:

class LoggerChangedMsg {
  public shared Logger logger() { return logger_; }
  private shared Logger logger_;
}
class SomeWorkingThread {
  void onLoggerChanged( immutable LoggerChangedMsg msg )
  {
    logger_ = msg.logger();
  }
}
...
send( new LoggerChangedMsg( createLogger() ) );

eao197 ★★★★★
()
Ответ на: комментарий от Vudod

Так он должен предусмотреть это. Для того const и предназначен.

forCe
()
Ответ на: комментарий от eao197

Ну не можете вы в немутабельном объекте иметь мутабельный. Но вы можете иметь в немутабельном объекте мутабельный слайс немутабельных данных - вполне возможно, что это ближе к тому, что вам нужно, хотя уверен вызовет нарекания в очередной раз :)

class Logger 
{
	void logMessage(string msg) const
	{
	}
}

class Bar
{
	const(Logger)[] logger_;
	void onLoggerChanged(const Logger new_logger) // const тут наиболее оптимален
	{
		logger_ ~= new_logger;
		logger_ = logger_[1..2];
	}
	void doLogMessage(string msg)
	{
		logger_[$-1].logMessage(msg);
	}
}

void main()
{
	auto bar = new immutable(Bar);
	return;
}
Компилятор доволен. Решение заключается в том, что вы не изменяете ссылку на предыдущий логгер, а добавляете ссылку на новый логгер в конец массива при этом удаляя старый логгер из массива (т.е. изменяете не саму ссылку - это нельзя сделать, а изменяете массив который перестает содержать ссылку, что полностью вписывается в концепцию immutability). И при работе всегда берете последний логгер из массива. Не совсем красиво правда и на мой взгляд, но я задумался над этой задачей только с вашей подачи. Возможно есть и более элегантное решение. Оверхед по сравнению с плюсами при добавлении логгера безусловно есть, но тут уж выбор за вами - или безопасная и простая работа с использованием immutable или ... :)

yetanother ★★
() автор топика
Ответ на: комментарий от yetanother

Ну не можете вы в немутабельном объекте иметь мутабельный.

Так он и не имеет. Это же ссылка на объект, а не сам объект. В языке с семантикой значений это принципиальная разница.

forCe
()
Ответ на: комментарий от forCe

Ну не можете вы в немутабельном объекте иметь мутабельный.

Так он и не имеет. Это же ссылка на объект, а не сам объект. В языке с семантикой значений это принципиальная разница.

+100500!

eao197 ★★★★★
()
Ответ на: комментарий от yetanother

Компилятор доволен.

На самом деле, компилятор доволен, только если нет обращения к Bar.onLoggerChanged. Стоит только такое обращение добавить, и все, ошибка компиляции:

/d33/f919.d(25): Error: mutable method f919.Bar.onLoggerChanged is not callable using a immutable object

Что вполне ожидаемо и логично.

PS. Что-то я начинаю подозревать, что знаю D лучше здешних моих оппонентов.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

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

Если вы хотите использовать immutable, то нужно использовать другую архитектуру. Компилятор не даст вам хранить мутабельную ссылку на логгер в немутабельном сообщении. Ссылка на мутабельный объект в немутабельном объекте не допустима по определению - в немутабельном объекте не может быть мутабельных данных в том или ином виде. Потому что выше Vudod, если не ошибаюсь, уже привел причины транзитивности, да и вы, eof, сами же объяснили причины транзитивности и тут же хотите нарушить ее. На самом деле вам без разницы хороший D или нет, потому что вы выбрали уже для себя язык и даже учитывая что D лучше вашего выбора, вы не можете уже перейти на него. Но для самолюбия продолжаете следить за языком и подтрунивать над людьми.

Еще раз - immutable очень сильная концепция, напрочь отсутствующая в плюсах. Да, она не позволяет вам реализовать мутабельный член immutable данных - но именно в этом и сила. В тех гарантиях, что дает язык - а не фреймворк.

yetanother ★★
() автор топика
Ответ на: комментарий от yetanother

immutable очень сильная концепция, напрочь отсутствующая в плюсах

Кажется, я просил привести хороший пример на тему силы концепции и отсутствия ее в C++.

forCe
()
Ответ на: комментарий от yetanother

Ссылка на мутабельный объект в немутабельном объекте не допустима по определению - в немутабельном объекте не может быть мутабельных данных в том или ином виде.

Так там и не будет мутабельных данных. Там будет ссылка на мутабельные данные.

forCe
()
Ответ на: комментарий от forCe

Еще раз вам повторяю, что immutable data не могут содержать mutable data в каком-либо виде. Если вы передаете в другой поток immutable структуру, в которой есть указатель на mutable данные, то все, никаких гарантий больше нет. Потому что любой может перейти по этому указателю на эти mutable данные. Но поскольку данные mutable, значит нет никаких гарантий что они корректны, так как они могли измениться где-то еще. А в D, благодаря транзитивности, любой также может перейти по указателю, но при этом он имеет гарантию, что данные корректные - потому что их никто не может изменить.

Именно поэтому в плюсах уже никогда не появится подобной концепции - потому что нужна транзитивность. Если для нового ключевого слова immutable ее ввести можно, так как оно новое, то для старого доброго const ее введение все сломает. А без const использовать immutable будет проблематичным.

yetanother ★★
() автор топика
Ответ на: комментарий от yetanother

Прекрасно понимаю, именно поэтому стараюсь избегать отвечать на вопросы - потому что человеку все равно недостаточно интересно.

Дело твоё, конечно. Хотя я и не согласен. Мне достаточно интересно, что взялся изучать язык по книге (единственной пока что, вроде?). Соответственно заранее узнать всякие интересные моменты, которые в книге могут и не упоминаться (она всё-таки 2010 года) очень даже интересно. И вполне помогают поддержать интерес на том же уровне.

а если пользуешься то вся ответственность на тебе.

Понятно.

fill_n и transform это из другой оперы - в том плане, что это аналоги fill и map из модуля std.algorithm.

Ну... вот те приятные возможности - это ведь просто встроенных в язык «сахар» для массивов. Удобно - спорить не буду, но в принципе, из той оперы.

DarkEld3r ★★★★★
()
Ответ на: комментарий от yetanother

Давайте мы перестанем повторять одно и то же. Все уже поняли ваши слова. Но вы так и не показали пример, а так же не пояснили, почему если я, скажем, сделаю на С++ иммутабельный ленивый список в стиле haskell'евских списоков, то он не будет иммутабельным?

forCe
()
Ответ на: комментарий от yetanother

Функция to из std.typecons является безопасной, она проверяет типы при преобразовании, например.

Ну... в С++ static и const касты всё статически проверяют и в рантаме оверхеда нет. Оверхед есть только от dynamic_cast. То есть нет смысла менять их дебаге/релизе. Ну кроме динамик каста.

А каст - он же голый каст.

Честно говоря, это мне кажется странным. Ладно в С++ из-за совместимости сишных каст есть. И то им не рекомендуется пользоваться. Нафига в D такое не понятано.

DarkEld3r ★★★★★
()
Ответ на: комментарий от forCe

forCe, ну вы же прекрасно понимаете что к чему. Зачем эта ваша упертость? В плюсах нет immutable типов, есть только const. И у const нет транзитивности. Если у вас поток принял переменную типа const T, то у вас нет никаких гарантий, что эта переменная не будет изменена где-то еще - это справедливо и в плюсах и на D. А если ваш поток принял переменную типа immutable Т, то D гарантирует потоку, что читать эту переменную можно безопасно в любой момент времени. В плюсах для достижения этого нужно соблюдать соглашение - например в виде фреймворка. И если хоть где-то будет передан неконстантный объект, то вы можете получить трудноулавливаемый баг. Который просто напросто невозможен в D. ИМХО, подход D мне кажется более логичным, стройным и правильным. Вы безусловно можете иметь другое мнение и я его приму как оно есть. Даже не думайте, что я кого-то буду уговаривать - я строго убежден, что выбор дело каждого. Я свой сделал, вы тоже. Поэтому удачи вам.

yetanother ★★
() автор топика
Ответ на: комментарий от DarkEld3r

Честно говоря, это мне кажется странным. Ладно в С++ из-за совместимости сишных каст есть. И то им не рекомендуется пользоваться. Нафига в D такое не понятано.

Честно говоря, если прочитать Александреску, то станет ясно, что cast оставлен на всякий случай, если по другому не получится. Поскольку он не работает для @safe режима, а это --- режим по умолчанию, видимо, он оставлен на случай чего-то низкоуровневого. Когда вырубаем сборщик мусора, проверки диапазонов массивов, включаем арифметику указателей при необходимости и понеслась душа в рай. Фактически, это возможность писать в стиле C.

Vudod ★★★★★
()
Ответ на: комментарий от Vudod

Честно говоря, если прочитать Александреску

Да в общем-то в первых примерах из книги сразу каст и встретил. Потому и удивляюсь.

Или нормальные безопасные приведения не сразу появились?

Поскольку он не работает для @safe режима

В смысле? Вот никакие настройки не трогал, но такое компилится (и даже «работает»):

struct Test
{
  int a;
  double b;
}

int main(string[] argv)
{
  auto val = 10;
  auto t = cast(Test)(val);
  writeln(typeid(t));
  writeln(t);

  return 0;
}

Когда вырубаем сборщик мусора, проверки диапазонов массивов

Дык, нормальные приведения через to всё равно статически работают?

DarkEld3r ★★★★★
()
Ответ на: комментарий от yetanother

Если у вас поток принял переменную типа const T, то у вас нет никаких гарантий, что эта переменная не будет изменена где-то еще

А если у меня поток принял struct my_type { const T data; }, то у меня такие гарантии есть.

А если ваш поток принял переменную типа immutable Т, то D гарантирует потоку, что читать эту переменную можно безопасно в любой момент времени

Замечательно. Когда я использую иммутабельные структуры данных, мне это гарантирует интерфейс этих структур, начисто лишенный мутабельных операций.

В плюсах для достижения этого нужно соблюдать соглашение - например в виде фреймворка.

В плюсах у меня там будет или потокобезопасный объект, который может быть изменен из какого-то другого потока и/или объект иммутабельной структуры данных. В зависимости от задачи.

forCe
()
Ответ на: комментарий от yetanother

которых const, тем более не транзитивный, никогда не даст.

Так ведь в D есть и не транзитивный const (headconst)?..

Ну и транзитивность ведь руками прописать можно. Несколько более многословно, но возможность-то есть. То есть тут не вижу преимуществ.

Насчёт immutable спорить не буду.

DarkEld3r ★★★★★
()
Ответ на: комментарий от Vudod

Это странное мнение, потому что очень часто самые обычные структуры бывает полезно передать куда-то, чтобы там (например, на клиентской стороне) их использовали. Если же их нельзя сделать целиком immutable, то это опасно, потому что там их попортят.

Хм, если код «той стороны» уже написан и менять его нельзя, то если он принимает данные не как immutable, то мы ведь как раз ничего сделать не можем. В смысле передать ничего иммутабельного не получится. Даже если реально код ничего менять не будет.

DarkEld3r ★★★★★
()
Ответ на: комментарий от forCe

А если у меня поток принял struct my_type { const T data; }, то у меня такие гарантии есть.

Нет у вас никаких гарантий. Потому что data может быть инициализирована не константным значением. И если T это не value-type, то никто вам не гарантирует безопасности. В том числе и eao197 - вы почитайте его пост про const и immutable в D. Он на самом деле понимает больше, чем говорит.

Замечательно. Когда я использую иммутабельные структуры данных, мне это гарантирует интерфейс этих структур, начисто лишенный мутабельных операций.

В D все действительно замечательно. Потому что в отличие от плюсов в нем не нужно на каждый тип городить интерфейс без мутабельных операций. Т.е. то, что в плюсах «гарантируется» на уровне соглашений, а значит требует определенных действий со стороны программиста, в D имеет железобетонные гарантии со стороны компилятора без каких-либо дополнительных усилий со стороны разработчика. В этом и сила, и красота решения.

В плюсах у меня там будет или потокобезопасный объект, который может быть изменен из какого-то другого потока и/или объект иммутабельной структуры данных. В зависимости от задачи.

Потокобезопасный объект требует примитивов синхронизации в том или ином виде, либо лок-фрии структур данных - ни то, ни другое не требуется при использовании immutable data в D (конечно std.concurrency использует примитивы синхронизации, но это скрыто от программиста). Это раз. А во-вторых, в стопицотый раз обращаю внимание, что в плюсах нет immutable типов. Все что у нас есть в плюсах это соглашение, что, ребята, давайте не будем изменять вот такие вот и вот такие вот данные. И стоит только одному нарушить это соглашение и вся стройная система рухнет. Такая система очень требовательна к сопровождению и const дает только лишь частичные гарантии - потому что const не запрещает инициализацию const T значением T - а это прямое нарушение гарантий. В D подобные соглашения не нужны. Тебе абсолютно не нужно знать почему твой предшественник писал вот это и вот это, каким соглашениям он следовал и т.д. - ты просто берешь и пишешь код. Потому что не получится у тебя сломать код из-за гарантий языка и компилятора. В независимости от задачи.

yetanother ★★
() автор топика
Ответ на: комментарий от DarkEld3r

В смысле? Вот никакие настройки не трогал, но такое компилится (и даже «работает»):

А что вас смущает? Что D является системным языком и позволяет программисту творить все что угодно, если он готов брать на себя ответственность за результаты?

Дык, нормальные приведения через to всё равно статически работают?

to(T) это шаблон, поэтому работает статически.

yetanother ★★
() автор топика
Ответ на: комментарий от DarkEld3r

Так ведь в D есть и не транзитивный const (headconst)?..

Это вы про что?

Ну и транзитивность ведь руками прописать можно. Несколько более многословно, но возможность-то есть. То есть тут не вижу преимуществ.

Как это вы ее пропишете? В чужом коде, например?

yetanother ★★
() автор топика
Ответ на: комментарий от DarkEld3r

Хм, если код «той стороны» уже написан и менять его нельзя, то если он принимает данные не как immutable, то мы ведь как раз ничего сделать не можем. В смысле передать ничего иммутабельного не получится. Даже если реально код ничего менять не будет.

Аргумент к чему? Ясное дело, что нельзя передать в код immutable данные, если код не готов их принимать и тем более не знает, что это такое. Именно поэтому в плюсах и не будет никогда этой концепции.

yetanother ★★
() автор топика
Ответ на: комментарий от yetanother

В D все действительно замечательно. Потому что в отличие от плюсов в нем не нужно на каждый тип городить интерфейс без мутабельных операций.

Мне представляется, что это не так. Из ваших слов (не только из ваших, но и из слов Vudod и других пользователей D) может сложиться впечатление, что const- и immutable- варианты типов в D получаются автоматически и это ничего не стоит. Думаю, что это так лишь для малого количества случаев, когда приходится работать с какими-то простыми структурами данных, где все атрибуты выставлены наружу как public и нет никаких методов.

Если же объект более сложный, то и картинка получается более сложная. Предположим, что у меня есть объект, который хранит список активных рабочих нитей. Каждая нить может быть в одном из трех состояний: инициализируется, работает, завершается. Соответственно в объекте находится три словаря для рабочих нитей: один для инициализирующихся, второй для работающих, третий для завершающихся. Когда нить меняет свое состояние, ее описание перемещается из одного словаря в другой.

Такой объект со словарями вряд ли будет выставлять наружу эти словари в качестве public-атрибутов. Наверняка они будут упрятаны вовнутрь как private-атрибуты, а наружу будут выставлены какие-то public-методы.

Так вот, насколько я помню D, эти методы должны иметь спецификаторы const или immutable для того, чтобы компилятор позволял их вызов через const/immutable ссылки на объект. И тут вы придете к тому же, что есть в C++: разделению методов по command/query принципу. Модифицирующие объект методы (commands) не будут иметь модификаторов const/immutable, а немодифицирующие методы (queries) будут иметь эти модификаторы. Все то же самое, что и в C++: хочешь дать пользователю возможность работы с твоим объектом через const-ссылку, позаботься о соответствующем интерфейсе объекта. И более того, к const-ссылкам теперь добавились еще и immutable-ссылки. И об этом так же нужно заботится программисту.

Причем есть у меня смутные подозрения, что в реальной жизни редко какие объекты будут предоставлять как const-интерфейс, так и immutable-интерфейс. Скорее всего, только const-ом и будут ограничиваться, как и в плюсах.

Ну и еще до кучи. Что-то мне не кажется, что immutable действительно удобен для больших и сложных структур данных. Хотя бы потому, что такие структуры данных должны как-то создаваться, и процесс их создания может требовать нескольких итераций, каждая из которых дополняет результат предыдущей. Т.е. на момент инициализации структура вовсе не будет immutable. И как дейтвительно большая и сложная структура будет в итоге объявлена immutable я не очень себе представляю (а вот как объявить ее const я представляю, так же, как и в C++).

В общем, сторонники D очень красочно и эмоционально говорят о достоинствах immutable, но не приводят примеров его реального использования. Можно что-либо подобное увидеть? Очень желательно на действительно сложной структуре данных.

eao197 ★★★★★
()
Ответ на: комментарий от yetanother

Нет у вас никаких гарантий. Потому что data может быть инициализирована не константным значением

Это не важно, сама data изменять не может - это будет UB.

И если T это не value-type, то никто вам не гарантирует безопасности.

В С++ все типы value-type, кроме ссылок &, но о них тут речь не идет.

В D все действительно замечательно. Потому что в отличие от плюсов в нем не нужно на каждый тип городить интерфейс без мутабельных операций. Т.е. то, что в плюсах «гарантируется» на уровне соглашений, а значит требует определенных действий со стороны программиста, в D имеет железобетонные гарантии со стороны компилятора без каких-либо дополнительных усилий со стороны разработчика. В этом и сила, и красота решения.

Вы мне покажете уже код на эту тему или так и будете писать лозунги?

Потокобезопасный объект требует примитивов синхронизации в том или ином виде, либо лок-фрии структур данных - ни то, ни другое не требуется при использовании immutable data в D

Да, тоже самое у меня будет, если я буду использовать иммутабельные структуры данных, не зависимо от языка.

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

Какие еще соглашения? В иммутабельном типе у вас нет мутабельных операций. Как вы будете его менять?

forCe
()
Ответ на: комментарий от yetanother

А что вас смущает? Что D является системным языком и позволяет программисту творить все что угодно, если он готов брать на себя ответственность за результаты?

Смущает две вещи. Мне казалось, что я уже обьяснил какие, но повторюсь:

1. Разве у меня в коде не «@safe режим» (пока не знаю, что это значит, но ничего для отключения не делал)? Тем не менее каст работает, хотя вы говорите, что не должен.

2. Не вижу никаких причин зачем в Д нужен аналог сишного каста, который что угодно перереработает во что угодно. Причём сам Александреску знакомит с ним до нормальных приведений. Хоть бы обозвали бы его как-то «пострашнее» типа «UnsafeCast». Кстати, в С++ reinterpret_cast, который тоже может что угодно куда угодно привести, константность снимать не может.

Понятное дело, что это всё не смертельно и ответ в духе «ну вот так сделали, хотя причин, кроме исторических, нет» меня вполне устроит.

P.S. Мне было бы удобнее на ты.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

2. Не вижу никаких причин зачем в Д нужен аналог сишного каста, который что угодно перереработает во что угодно.

Историческое наследие, полагаю. D всегда позиционировался как полноценная замена C++, в том числе и для низкоуровневого кода, а так же для достижения максимальной производительности. И там и там аналоги сишных кастов оказываются полезными.

eao197 ★★★★★
()
Ответ на: комментарий от yetanother

Это вы про что?

headconst

В чужом коде, например?

В чужом коде не пропишу, конечно.

Но пользу от транзитивного конста я так и не понял (пользу immutable более-менее понимаю).

DarkEld3r ★★★★★
()
Ответ на: комментарий от yetanother

в D имеет железобетонные гарантии со стороны компилятора без каких-либо дополнительных усилий со стороны разработчика. В этом и сила, и красота решения.

Не такие и железобетонные, всё-таки каст применить можно и компилятор проглотит это.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Это вы про что?

headconst

В D нет ни headconst, ни tailconst. В const-FAQ эти понятия просто приведены для пояснения. При этом явно говорится, что D этого не поддерживает: «D does not have head const (the headconst is there just for illustrative purposes)»

eao197 ★★★★★
()
Ответ на: комментарий от DarkEld3r

Не такие и железобетонные, всё-таки каст применить можно и компилятор проглотит это.

Это, кстати, странно. Потому что по-идее cast принадлежит system mode, т.е. не должен работать в safe. По крайней мере, я встречал такое утверждение несколько раз.

Vudod ★★★★★
()
Ответ на: комментарий от eao197

В const-FAQ эти понятия просто приведены для пояснения.

Да, невнимателен был.

Но всё-таки возможность ограниченно (с помощью скобок) применять конст ведь есть.

DarkEld3r ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.