Comments 27
Неплохое решение проблемы того, что класс-значение не обладает полиморфизмом (необходимы указатели) — который у языков типа Java|C# собственно и нет — там просто возвращается интерфейс.

Но у конкретно этого подхода есть проблема — возвращаемый тип не указывается, что неприятно с точки документации кода (чтобы определить тип приходится погружаться в исходный код функции). Мне кажется было бы лучше писать что-то типа? extends Iterable (а-ля шаблоны с ограничениями) чтобы с одной стороны, было указание типов, с другой — сохранился бы статический полиморфизм.
В 99% случаев использования, возвращается тип, реализующий range, пользователь не должен задумываться какой именно это range, почти все функции в Phobos работают с range'ами. В остальных экзотических случаях, да, придется явно описать в документации какой интерфейс реализует скрытый тип. Идея с шаблонами — статическими интерфейсами очень интересна, думаю можно реализовать это с помощью рефлексии как library solution.

Также можно поизвращаться и через рефлексию считать все поля и методы Voldemort типа и сконструировать свой (через mixin), а заодно функцию, которая превращает экземпляр Voldemort типа в кастомный. В общем, огромное поле для ненормального программирования.
>В 99% случаев использования, возвращается тип, реализующий range, пользователь не должен задумываться какой именно это range

Так я же говорю об интерфейсе, а не о реализации. Информация о том что структура итерабельна — оно должно быть в шапке функции, рефлекшн тут вообще не причем, я про читабельность и поддерживаемость кода говорю.
На самом деле синтаксических средств для такой маркировки возвращаемого типа в D не существует, а структуры не могут реализовывать интерфейсы.
В D все классы — полиморфные референсные типы, как и в C#. Voldemort types это, как правило, структуры.
Соответственно, можно было бы вернуть интерфейс, но это было бы чертовски неудобно, ведь стандартная библитека практически не использует OOP, предпочитая статический полиморфизм через duck typing и шаблоны. Во всех случаях, когда используется Voldemort type, пользователь _не должен_ знать точного типа, с которым работает, это преднамеренно.
>предпочитая статический полиморфизм через duck typing и шаблоны.

Вот это мне кажется и плохо. В том смысле что duck typing конкретно здесь — не нужен (и вообще очень редко нужен). Мне как раз всегда было обидно, что статический полиморфизм и динамический разделяют — хотя логически между ними нет разницы.
Между ними огромная разница практически, а D — практичный язык. Динамический полиморфизм очень сильно бьёт по производительность за счёт лишнего indirection. Статический — за счёт template bloat. В целом нужно аккуратно выбирать, что из этого хуже. Специфика работы с range-типами располагает к первому варианту.

Более того, динамический полиморфизм в базовых частях стандартной библиотеки добавил бы ещё одну причину, по которой её нельзя использовать в embedded & Co, а это и так болезненная тема для Phobos.
>Между ними огромная разница практически, а D — практичный язык.

Вы меня не поняли. Я прекрасно знаю чем отличается динамический от статического полиморфизма, вы не на тот вопрос отвечаете. Меня расстраивает, что эти два типа полиморфизма так сильно различают синтаксически, в то время как можно было свести разницу к миниму, сохранив преимущества статической явной типизации и в статическом полиморфизме.
Делать их слишком схожими синтаксически опасно как раз таки из за различий «под капотом». Я довольно давно уже пытаюсь протолкнуть идею разрешить использовать interface в качестве constraint, в духе Rust traits, благо это сделать не очень трудно. Тогда можно было бы разрешить синтаксис в духе auto!InputRange, аналогично для параметров функций. Однако это предложение пока что не вызвало особо энтузиазма, а на pull request меня не хватает.
Такая фича сделала бы много пользы, например, я столкнулся с проблемой проверки наличия определенных методов у структур при реализации compile-time бекэндов для велосипедного сериализатора. Это можно сделать и специальным шаблоном, который в guarding выражениях проверит наличие методов, но через такой синтаксический сахар намного меньше monkey job.
Ну разница в сахаре между
void func(T : InputRange)(T range)
{
...

и
void func(T)(T range)
    if (Implements!(T, InputRange))
{
...

не очень велика, но сообщение об ошибке в первом варианте будет намного понятнее.
Первый случай не пройдет со структурами, а Implements я нигде не могу найти в Phobos, а вроде очень полезная вещь.
Я как раз и предлагал расширить первый случай для структур в контексте duck typing, возможно, с другим спецификатором вместо ":". Увы, не пошло :)

Пример реализации Implements из личных запасов — dpaste.1azy.net/6d8f2dc4. Хочу предложить в Phobos, но нужно оформить в более строгом виде, стандартная библиотека всё-таки.
Логически между ними довольно большая разница — ограничения сильно разные. Разницы практически нет в языках с тайпклассами, но только за счет того, что и на динамику, и на статику есть довольно сильные ограничения.
Стоит отметить, что подобные типы не вполне «призрачные», их можно получить и инстанциировать вне функции через typeof, никаких дополнительных преград кроме синтаксического неудобства тут не ставится.
Если только в этих структурах нет замыканий, иначе будет ошибка компиляции. В посте было про этот «хак».
А, пардон, просмотрел. Я к тому, что в сообществе до сих пор нет согласия насчёт того, как компилятор должен себя вести в этом случае, как это часто и бывает со случайно открытыми фичами. Поэтому не стоит полагаться на такое поведение :)
Было интересно прочитать. Пишите еще про D. Уже давно заглядываюсь на него, как на замену C++.
Такое захватывающее название, а по сути-то?

В рантайме это решается интерефейсами (Iterable), в компайл-тайме — меткой «unspecified-type» в документации (например), чего вполне достаточно. От совсем буратин можно спрятать конструктор вспомогательного класса, чтобы не конструировали чего не надо, но мне это кажется уже излишним.
в компайл-тайме — меткой «unspecified-type» в документации (например), чего вполне достаточно.


Так речь идет о внезапной возможности решить проблему более элегантным способом, итого из пространства имен целиком убираются вспомогательные вещи, которые в идеале должны быть доступны только реализации.
> Типы, которые можно использовать, но нельзя назвать.
А я, по названию, подумал сначала о подсчете ссылок перед убийством =)
А в C++ мы делаем так:

auto rand_generator(unsigned seed)
{
    return [=]() mutable
    {
        seed = seed * 1103515245 + 12345;
        return ((seed / 0x10000) * seed) >> 16;
    };
}

int main()
{
    vector<unsigned> numbers;
    generate_n(back_inserter(numbers), 10, rand_generator(1337));
}
По сути здесь тоже имеет место использование анонимных типов, которые можно использовать, но нельзя назвать :)
Давно не работал с C++, но скомпилировать получилось вот такой вариант:
#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

auto rand_generator(unsigned seed) -> function<unsigned (void)>
{
  return [=]() mutable
  {
    seed = seed * 1103414245 + 12345;
    return ((seed /0x10000) * seed) >> 16;
  };
}

int main()
{
  vector<unsigned> numbers;
  generate_n(back_inserter(numbers), 10, rand_generator(1337));

  for( vector<unsigned>::const_iterator i = numbers.begin(); i != numbers.end(); ++i)
    cout << *i << ' ';

  return 0;
}


Без «trailing return type» ни gcc 4.8, ни vs2012 компилировать не хочет. Также хочу узнать, существует ли способ вывести вектор на экран лучше, чем лобовым циклом?

Просто для сравнения, аналогичная программка на D, просто для сравнения синтаксиса языков, по мне C++11 лямбды очень многословны:
import std.range;
import std.stdio;
import std.algorithm;

auto rand_generator(uint seed)
{
	return ()
	{
		seed = seed * 1103414245 + 12345;
		return ((seed / 0x10000) * seed) >> 16;
	};
}

int main()
{
	writeln( repeat(rand_generator(1337), 10).map!(x => x()) );
	return 0;
}


Да, новые лямбды в C++11 можно назвать Voldemort типами. Однако в D они обычно являются структурами, с ними удобнее работать, чем с лямбдами, и структуры не выделяют память в куче.
Only those users with full accounts are able to leave comments. Log in, please.