migmit: (Default)
[personal profile] migmit
Ну что, попробовал я таки этот Swift. Скачал бету XCode, распаковал, запустил. И доложу я вам: это штука с огроменным потенциалом. В смысле, ни фига не работает.

Я, честно сказать, думал проверить ихние дженерики своим любимым тестом, который C++ не прошёл, а, скажем, Java — вполне. Наивный.

Дело в том, что когда вы пишете код в XCode, то вам показывают места ошибок. Это очень удобно. Однако делается это путём запуска компилятора в фоне. А компилятор сей страдает падучей. И когда он сегфолтится — а это происходит довольно часто — весь XCode вылетает нафиг.

Дальше ещё веселее. Вы запускаете XCode заново, он восстанавливает тот же файл, снова прогоняет его через компилятор, тот снова сегфолтится, и XCode снова вылетает.

Я не смог запилить такой код, который проверял бы мой тест и при этом не сегфолтился.

Так что пока ихняя бета (которая beta than nothing) отправляется в корзину. Увы.

Date: 2014-06-07 10:04 pm (UTC)
sergey_cheban: (Аракчеев)
From: [personal profile] sergey_cheban
> Я, честно сказать, думал проверить ихние дженерики своим любимым тестом, который C++ не прошёл, а, скажем, Java — вполне
Что за тест?

Date: 2014-06-07 10:49 pm (UTC)
From: [identity profile] migmit.livejournal.com
http://migmit.livejournal.com/32688.html

Если подробно — нужно написать две функции (или что-то в таком роде): scalarProduct и, скажем, main. Функция scalarProduct должна принимать две последовательности (это могут быть массивы, связные списки, или ещё что-то — не важно) целых чисел и вычислять их скалярное произведение. При этом она должна падать, если длины этих последовательностей различны. Важное (критически) уточнение: это должно происходить НА СТАДИИ КОМПИЛЯЦИИ!!!

Функция main должна, не зная, как работает scalarProduct, генерировать две последовательности (простейший вариант — запрашивать их у пользователя) заранее неизвестной (но одинаковой!) длины, после чего обращаться к scalarProduct и вычислять их скалярное произведение.

Date: 2014-06-08 07:47 am (UTC)
sergey_cheban: (Аракчеев)
From: [personal profile] sergey_cheban
Итого:
1. Должны генериться два вектора.
2. Длина векторов будет известна только в рантайме.
3. Падать должно в compile-time.
Увы, чудес не бывает. Так что либо мы вместо пары векторов на самом деле генерируем что-то типа vector<pair<T,T>> (а то, что он в некоторых языках выглядит не как вектор пар, а как пара векторов - это только синтаксический сахар), либо имеем проверку в рантайме.
Edited Date: 2014-06-08 07:47 am (UTC)

Date: 2014-06-08 08:23 am (UTC)
From: [identity profile] migmit.livejournal.com
А теперь посмотри по ссылке. Там сделано для C#. Для Java это переписывается практически дословно. В хаскеле это совсем тривиально, в OCaml необходимо немного поприседать.

Идея в том, чтобы сделать последовательности разной длины значениями разных типов. Тогда наша функция может быть дженериком, который принимает два аргумента любого — но одного и того же — типа, если только он соответствует некоторым ограничениям (например, экспортирует определённый интерфейс).

Как следствие, набор типов, которые нужно будет использовать в рантайме, становится неограниченным — но с настоящими дженериками это не страшно. А вот шаблоны C++ не справляются. И Rust-овские "дженерики" не справляются (потому что они там шаблоны, на самом деле, только без возможности частичной специализации).

Date: 2014-06-08 06:50 pm (UTC)
sergey_cheban: (Аракчеев)
From: [personal profile] sergey_cheban
Да я смотрел по ссылке. Идея в том, что либо мы должны как-то объяснить компилятору, что у нас переменные одного типа (неизвестного, поскольку длина векторов неизвестна, но одного), либо нам придётся-таки делать проверку в рантайме. Так вот, чтобы поддерживать одинаковость типов, тебе приходится везде обрабатывать вектора вместе. А в этом случае разница между парой векторов и вектором пар исчезает.

PS. Я в Москву на Мейерса еду, так что не удивляйся, если пропаду дня на три.

Date: 2014-06-08 06:55 pm (UTC)
From: [identity profile] migmit.livejournal.com
> мы должны как-то объяснить компилятору, что у нас переменные одного типа

Да.

> А в этом случае разница между парой векторов и вектором пар исчезает.

Не согласен.

Автор scalarProduct может вообще не рассчитывать на подобное использование. Он, может, вообще ожидал, что функция будет вызываться только для векторов с заранее известной длиной (и в этом случае шаблонов вполне хватило бы). Сделал, запихал в библиотеку — готово.

А мы взяли эту библиотеку и применили её вот таким образом.

Date: 2014-06-12 12:26 pm (UTC)
sergey_cheban: (Аракчеев)
From: [personal profile] sergey_cheban
> Автор scalarProduct может вообще не рассчитывать на подобное использование.
Я не уверен, что лёгкость использования библиотек/микроскопов не по назначению может считаться аргументом в пользу чего-либо. Так что давай всё-таки предположим, что автор scalarProduct знал, что делал.

А если нет, то я предложу другую задачу: взять два вектора из файлов, в рантайме проверить, что их длины равны, если не равны - ругнуться, а если равны - перемножить. Рискну предположить, что такое, в зависимости от языка, либо не делается вообще, либо делается с неявным использованием RTTI (т.е. с overhead'ом).

Date: 2014-06-08 10:38 pm (UTC)
From: [identity profile] alex-public.livejournal.com
Ваша задачка тривиально решается на C++ (кстати, это решение я уже давно выкладывал на rsdn):
#define cint struct{const int value;}
template<typename T> class Cons{
    vector<int> data;
    Cons()=default;
    template<typename S, typename F> friend Cons<S> MakeCons(S s, F f);
    template<typename S> friend int ScalarProduct(const Cons<S>& c1, const Cons<S>& c2);
};
template<typename S, typename F> Cons<S> MakeCons(S s, F f)
{
    Cons<S> r;
    for(int i=0; i<s.value; i++) r.data.push_back(f(i));
    return r;
}
template<typename S> int ScalarProduct(const Cons<S>& c1, const Cons<S>& c2)
{return inner_product(c1.data.cbegin(), c1.data.cend(), c2.data.cbegin(), 0);}
int main()
{
    int n;
    cin>>n;
    cint size{n};
    auto cons1=MakeCons(size, [](int i){return i;});
    auto cons2=MakeCons(size, [](int i){return i+1;});
    //cint size1{n+1};
    //auto cons2=MakeCons(size1, [](int i){return i;});//а такое не будет компилироваться
    cout<<ScalarProduct(cons1, cons2)<<endl;
} 

Причём такой подход позволяет спокойно создавать компоненты для произведения не обязательно внутри одной функции, как в примерах C#/Java. Ну а про расход памяти и быстродействие думаю можно даже не упоминать — они тут получаются вообще не сравнимыми... )

Что касается Swift'a, то по набору качеств (во всяком случае судя по документации - саму среду я не ставил и не собираюсь) он больше всего напоминает обрезанный D (без метапрограммирования, без интроспекции, без исключений и поддержки многопоточности). Однако даже такой обрезанный D в любом случае выглядит лучше чем C#/Java...

А вот настолько глючной компилятор - это конечно беспредел...
Edited Date: 2014-06-08 10:57 pm (UTC)

Date: 2014-06-09 04:38 am (UTC)
From: [identity profile] migmit.livejournal.com
Ну, пример не смешной. В нём столько очевидно неверного, что не знаешь, с чего начать. Ну, например:
    decltype(size) size1{n+1};
    auto cons3 = MakeCons(size1, [](int i) {return i+1;});
    cout << ScalarProduct(cons1, cons3) << endl;

Date: 2014-06-09 01:04 pm (UTC)
From: [identity profile] alex-public.livejournal.com
Вообще то целью было написание проверяющей системы, а не противодействие её сознательному взлому программистом (что в принципе невозможно в языке с указателями и привидением типов, и это я не только про нашу задачку). Однако конкретно эта ваша проблема решается тривиально:
#define cint(x) [v=int(x)]{return v;}
...
auto size=cint(n);//теперь записываем так
decltype(size) size1=cint(n+1);//тут компилятор пошлёт )))

Date: 2014-06-09 07:53 pm (UTC)
From: [identity profile] migmit.livejournal.com
Помилуйте, если бы речь шла о сознательном противодействии, то всё было бы куда проще, был бы банальный const_cast или C-style cast. Я уже уменьшил свои требования и согласен не рассматривать хотя бы то, что традиционно считается грязным хаком (хотя и всё равно все делают). Но decltype — штука совершенно безопасная, она для того и придумана.

OK, как вы будете закрываться от того, что кто-то вместо cint использует написанный с совершенно другой целью макрос
#define mint(x) [v = int(x)] () mutable {return v++;}

?

Да, мне интересно, как далеко вас заведёт DTDD.

> конкретно эта ваша проблема

Извините, это ВАША проблема. Не моя.
Edited Date: 2014-06-09 07:53 pm (UTC)

Date: 2014-06-09 09:36 pm (UTC)
From: [identity profile] alex-public.livejournal.com
Я согласен, что decltype - это нормальная вещь. Потому и показал исправление.

А вот против этого вашего mint я уже защиту показывать не буду (хотя она тут же возникла в голове), т.к. это уже просто бред. Это приблизительно тоже самое, что требовать нормальной работы функции записи в файл при передаче ей дескриптора файла, открытого на чтение.

Кстати, если рассматривать уже такой уровень претензий, то что будет, если передать null вместо одного из Cons'ов в вашем примере на C#? )

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

Date: 2014-06-10 07:06 am (UTC)
From: [identity profile] migmit.livejournal.com
> Это приблизительно тоже самое, что требовать нормальной работы функции записи в файл при передаче ей дескриптора файла, открытого на чтение.

Кстати, есть разработки, которые это учитывают. И не скомпилируют такую функцию. Светлой памяти Microsoft Vault, например.

> то что будет, если передать null вместо одного из Cons'ов в вашем примере на C#?

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

Date: 2014-06-08 09:46 pm (UTC)
From: [identity profile] bik-top.livejournal.com
> И когда он сегфолтится — а это происходит довольно часто — весь XCode вылетает нафиг.

А если писать код не в Xcode, а Sublime Text 2?

Date: 2014-06-09 04:25 am (UTC)
From: [identity profile] migmit.livejournal.com
Понятия не имею.

Date: 2014-06-17 01:02 am (UTC)
From: [identity profile] rumataestor.livejournal.com
Rust с твоей задачкой справляется. На мой взгляд это единственный заслуживающий внимания язык из недавно появившихся.

Date: 2014-06-17 07:42 am (UTC)
From: [identity profile] migmit.livejournal.com
Не справился, когда я на него смотрел. И на заведённый баг разработчики ответили "это так и должно быть".

Date: 2014-06-18 01:06 am (UTC)
From: [identity profile] rumataestor.livejournal.com
Хм, я только пробовал до применения scalar_product к заранее подготовленным значениям. Как оказалось, построить такой вектор произвольной длины не прокатывает ещё на этапе компиляции.

Кстати, решение на C# работает только до определённого предела. StackOverflow уже на глубине 10000 приходит.