заметки на полях (фильтрации)
May. 8th, 2013 01:19 amНа языках программирования можно ввести отношение частичного порядка "А < Б", когда с языка А на язык Б пересесть можно, и это ощущается как прогресс, а вот обратно возвращаться очень неохота и мучительно. Если не ошибаюсь,
lionet в свое время заметил, что в этом отношении заметны две вершины - хаскель и лисп, с их высоты все остальные языки кажутся недостаточно хорошими. Но занятно другое: у этого отношения есть также два дна, по сравнению с которыми все другие языки выглядят превосходными, - это PHP и C++. :)
После ряда других языков мне заставить себя писать что-то на С++ очень сложно, но иногда такая необходимость возникает. Помогает сгладить моральные мучения лишь возможность найти в языке крупицы чего-то хорошего. Нынче вот взялся за новую реализацию своего фирменного super resolution движка, и что меня сейчас радует и выручает, это присутствующие в языке элементы зависимых типов. У меня код оперирует блоками разных размеров и векторами разной точности: это могут быть целые координаты в кадре низкого разрешения, в кадре высокого разрешения, а также координаты с полупиксельной и четвертьпиксельной точностью. Плюсовые шаблоны позволили описать эти вещи как семейства типов, индексированные целочисленными значениями, т.е. натурально зависимые типы получились:
В результате блоки разного размера - это разные типы, и векторы разной точности - разные типы, реально очень помогает не запутаться. Плюс компилятору подспорье: у многих циклов число итераций теперь известно статически, можно хорошо оптимизировать. В иных языках для таких вещей можно использовать phantom types, но там может быть сложнее сделать функцию refine, переводящую вектор на следующий уровень точности, в соседний слой семейства. Все-таки очень удобно, когда с параметром типа можно делать всякую арифметику и использовать его сразу на двух уровнях: типов и выражений.
К слову о зависимых типах. Одна тривиальная мысль о них мне лично оказалась весьма полезной для понимания. Мы привыкли в функциональных языках обозначать тип функции из А в В как А -> B, где А и В какие-то конкретные типы вроде Bool и Int, элементами которых служат значения вроде true и 2. Теперь добавим в систему типов еще один тип, назовем его Type, элементами которого являются типы. Тогда A -> Type будет просто типом функции, которая каждому элементу А сопоставляет какой-то тип. Это и получится зависимый тип, и именно так он и обозначается в соответствующих языках. Например, пишут B : A -> Type и говорят, что В - это зависимый от А тип, или семейство типов, индексированное значениями из А. Но эту же запись можно воспринимать и буквально - как обычную функцию, просто кодомен у нее не совсем обычный.
После ряда других языков мне заставить себя писать что-то на С++ очень сложно, но иногда такая необходимость возникает. Помогает сгладить моральные мучения лишь возможность найти в языке крупицы чего-то хорошего. Нынче вот взялся за новую реализацию своего фирменного super resolution движка, и что меня сейчас радует и выручает, это присутствующие в языке элементы зависимых типов. У меня код оперирует блоками разных размеров и векторами разной точности: это могут быть целые координаты в кадре низкого разрешения, в кадре высокого разрешения, а также координаты с полупиксельной и четвертьпиксельной точностью. Плюсовые шаблоны позволили описать эти вещи как семейства типов, индексированные целочисленными значениями, т.е. натурально зависимые типы получились:
#define VP_LOWRES 1
#define VP_HIRES 2
#define VP_HALF 3
#define VP_QUARTER 4
template <int Prec>
class Vec
{
public:
int x, y;
Vec(int vx, int vy) : x(vx), y(vy) {}
Vec operator+(Vec<int Prec> &a) { return Vec<Prec>(x + a.x, y + a.y); }
Vec operator-(Vec<int Prec> &a) { return Vec<Prec>(x - a.x, y - a.y); }
Vec<Prec + 1> refine() { return Vec<Prec + 1>(x*2, y*2); }
...
};
class Plane
{
...
template<int W> void readBlockQP(Vec<VP_QUARTER> v, MonoBlock<W> &block);
...
}
В результате блоки разного размера - это разные типы, и векторы разной точности - разные типы, реально очень помогает не запутаться. Плюс компилятору подспорье: у многих циклов число итераций теперь известно статически, можно хорошо оптимизировать. В иных языках для таких вещей можно использовать phantom types, но там может быть сложнее сделать функцию refine, переводящую вектор на следующий уровень точности, в соседний слой семейства. Все-таки очень удобно, когда с параметром типа можно делать всякую арифметику и использовать его сразу на двух уровнях: типов и выражений.
К слову о зависимых типах. Одна тривиальная мысль о них мне лично оказалась весьма полезной для понимания. Мы привыкли в функциональных языках обозначать тип функции из А в В как А -> B, где А и В какие-то конкретные типы вроде Bool и Int, элементами которых служат значения вроде true и 2. Теперь добавим в систему типов еще один тип, назовем его Type, элементами которого являются типы. Тогда A -> Type будет просто типом функции, которая каждому элементу А сопоставляет какой-то тип. Это и получится зависимый тип, и именно так он и обозначается в соответствующих языках. Например, пишут B : A -> Type и говорят, что В - это зависимый от А тип, или семейство типов, индексированное значениями из А. Но эту же запись можно воспринимать и буквально - как обычную функцию, просто кодомен у нее не совсем обычный.
no subject
Date: 2013-05-07 11:52 pm (UTC)А ведь когда-то была многообещающая платформа. Но дальше каждый сам делает свои выводы.
no subject
Date: 2013-05-08 06:10 pm (UTC)Сделайте мне на практически любом языке тривиальный метод, который я написал в каменте выше:
http://thedeemon.livejournal.com/64155.html?thread=1062811#t1062811
no subject
Date: 2013-05-08 11:04 pm (UTC)Ладно, хрен с ним с кодом.
Суть await в том, что осуществляется некое CPS-преобразование.
В лиспах это делается макросами.
В хаскеле вообще готовая do-notation. Т.е. клиентский код со всякими readByte будет идентичен в синхронной и асинхронной версии, изменятся только монады.
В питоне достаточно b = yield s.ReadByte()
В языках с замыканиями, но без автоматического CPS - for заменяется на функции высших порядков, а преобразование пишется как есть: s.ReadByte(function(b){res ^= b; ...
В C++ те же замыкания, но можно обернуть в кошерные макросы.
Идеальным решением были бы полноценные coroutines, но авторы C# уже как-то плакались в блогах, что в компиляторе многокода, а основной целевой аудитории и так достаточно порционных хаков (yield, await, ...).
no subject
Date: 2013-05-08 11:17 pm (UTC)Он очень жизненный для любого сервера, где есть чтение и запись в поток, и состояние сессии.
Например не далее как месяц назад я таким макаром довольно быстро (10 рабочих часов вместе с багфиксами) сделал вполне работоспособный наколенный web server (готового не было потому что это windows phone), который умеет static content, dynamic content, и JSON RPC.
Примера я так и не увидел.
>b = yield s.ReadByte()
Асинхронного IO что-то не видно.
>s.ReadByte(function(b){res ^= b;
Прекрасно, я ждал подобного примера.
Теперь заверните это пожалуйста в цикл до 100, и не забудьте что на каждый запрос нужно ещё отправить ответ, так же асинхронно.
no subject
Date: 2013-05-08 11:43 pm (UTC)no subject
Date: 2013-05-08 11:49 pm (UTC)Спасибо, такое мне не нужно.
>перестать видеть сложности в таких вещах
Не вижу никаких сложностей, у меня есть C# 5.0.
Однако для вас очевидно сложность есть и немаленькая, раз вы не сумели "на практически любом языке" написать аналогичные 5 строк кода, решающих тривиальную в общем-то задачу "последовательно поксорить 100 поступающих из сети байт".
Но дальше каждый сам делает свои выводы ©
no subject
Date: 2013-05-09 12:06 am (UTC)no subject
Date: 2013-05-09 08:19 am (UTC)no subject
Date: 2013-05-10 04:26 pm (UTC)no subject
Date: 2013-05-10 06:10 pm (UTC)Луговскийволшебник, мне просто влом. :)no subject
Date: 2013-05-10 04:14 pm (UTC)Собственно чтобы было понятно: Питон я люблю, но разбираюсь в его деталях даааалеко не так хорошо как в плюсах; СиШарп, как и дотнет - не юзал (да и не стремлюсь) и видел только разный случайный чужой код типа как на презенташках и в msdn-е.
Так вот:
Я, питоновский yield - еще ниразу не юзал (и по-моему даже не видел до сего момента) и потому заглянул в гугл. Прочел все 3,5 строчки описания в документации, одну статейку на Хабре и сейчас в процессе чтения pdf-ки на овер 150 страниц, где этот самый yield описывается...
Вопрос:
из ит ок, что находясь на 40-й страничке pdf-ки, мне до сих пор не очевидно каким боком можно заюзав "b = yield s.ReadByte()", получить нормальный ассинхронный цикл ввода-вывода без использования убогих практик с юзанием чего-то вроде Sleep() из винапи? Ну что-бы эта штука не крутила цикл постоянно, постоянно-же кушая проц.
И это при том, что уже даже пара каментов soonts - все прояснила про шарповый async/await, и мне уже успела эта штука понравится.
no subject
Date: 2013-05-10 04:39 pm (UTC)В C# тоже есть yield, но с Enumerable-типизацией и без возможности возвращать значения в coroutine, поэтому им пришлось городить повторный зоопарк с await.
no subject
Date: 2013-05-09 12:04 am (UTC)насчет конкретно асинк-эвейта: имхо это тупо маркетинг. главное все же рантайм.
no subject
Date: 2013-05-09 05:11 am (UTC)no subject
Date: 2013-05-09 08:23 am (UTC)Стало в разы проще написать и отладить эффективный асинхронный параллельный I/O-bound код.
"Эффективный" = минимальный CPU usage при заданном throughput (особенно актуально на смартфонах где CPU usage=батарейка), или максимальный throughput (особенно актуально на серверах).