заметки на полях (фильтрации)
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 06:46 pm (UTC)C# третья.
no subject
Date: 2013-05-07 07:57 pm (UTC)Пехепе - вполне себе сравнительно неплохой невыразительный язык :)
no subject
Date: 2013-05-07 08:46 pm (UTC)это ж Pi-тип? но ведь есть и Sigma!
no subject
Date: 2013-05-07 09:03 pm (UTC)Впрочем, слова «недостаточно хорошими» действительно нуждаются в уточнении — для чего и по каким критериям.
no subject
Date: 2013-05-07 09:26 pm (UTC)Во-первых, в C# есть очень полезные вещи, которые даже не снились конкурентам, например LINQ и async-await.
Во-фторых, язык практически не навязывает стиля программирования. Можно unsafe code с арифметикой указателей и ручным управлением памятью, можно динамический язык вообще без проверки имён методов, даже эту вашу функциональщину можно.
И наконец, высокое качество рантайма и прилагаемой библиотеки классов.
no subject
Date: 2013-05-07 10:02 pm (UTC)*в священном ужасе выбросил SICP в окно*
no subject
Date: 2013-05-07 10:03 pm (UTC)2. async-await собирается в хаскеле или том же clojure (Lisp) из примитивных фич языка. В C# это добавили в качестве конструкции языка. Тоже говорит не о выразительности языка, а о "выраженности" — как в PHP: чего напихали в сам язык, то и выражается на этом языке.
2. > Во-фторых, язык практически не навязывает стиля программирования. — то, что у языка нет ограничений, это ещё не повод называть его элегантным. Минимум ограничений и максимум свободы — у языка ассемблера.
Про рантайм и библиотеку классов умолчу пока.
no subject
Date: 2013-05-07 10:35 pm (UTC)Даже функциональщина в C# выражается неуклюже (в нем вообще есть хотя бы const?), насколько я могу судить из своих скудных знаний о нем.
no subject
Date: 2013-05-07 10:46 pm (UTC).NET 3.5 - 2007.
DSH package - первая версия с пометкой "proof of concept implementation only" 2011.
Ты уверен, что ноги растут именно в ту сторону, в которую тебе кажется?
>собирается в хаскеле или том же clojure (Lisp) из примитивных фич языка
Ты сам использовал async-await в реальной жизни?
Подозреваю что нет.
За разумное число строк кода ты не соберёшь ни из чего аналог.
Там огромную кучу сложности аккуратно спрятали за элегантной конструкцией из двух ключевых слов: маршалинг исключений, scheduler интегрированный в thread pool, интеграцию почти со всеми асинхронными API из framework, минимизацию context switches, synchronization contexts, поддержку cancellation, и много чего ещё.
>Минимум ограничений и максимум свободы — у языка ассемблера.
Я говорил не про свободу простреливать себе ноги, а про свободу выбора стиля программирования.
В ассемблере с этим даже хуже чем в Java.
no subject
Date: 2013-05-07 10:59 pm (UTC)О да. Ищи LINQ здесь (статья 2009 года). http://fprog.ru/2009/issue1/lev-walkin-editorial/
> За разумное число строк кода ты не соберёшь ни из чего аналог. Там огромную кучу сложности аккуратно спрятали
Так об этом и речь. Огромная махровая куча, которую, чтобы правильно готовить, нужно решать в языке и в инфраструктуре кучу проблем. Где здесь элегантность языка просвечивает? Вместо того, чтобы выразить нечто в библиотеке, пришлось им решать огромную кучу сложности и прятать в сам язык, потому что пользователь C# сам эту сложность соорудить не в состоянии.
Вместе с тем, примитивы Erlang'а (промышленный стандарт в плане элегантности подхода к concurrency) делаются на Haskell всего лишь в виде библиотеки.
Подход "порешаем кучу сложностей и встроим конструкцию прямо в язык" имеет право на существование. Только, во-первых, этот подход периодически сбоит (чего стоит одна история с провалившейся попыткой решить "все сложности", когда пытались прикрутить STM к .Net), а во-вторых, это ничего общего не имеет с элегантностью языка. Практично? Да. Удобно? Да. Но не более.
> В ассемблере с этим даже хуже чем в Java.
Верно. В C# тоже с этим не всё так хорошо как в лиспе, например.
no subject
Date: 2013-05-07 11:29 pm (UTC)Концепции и парадигмы — это для архитектурных астронавтов термины, я ими не пользуюсь.
Реализация Linq2SQL появилась в Haskel на 4+ года позже, чем в C#.
>Где здесь элегантность языка просвечивает?
В том и элегантность, что огромную кучу сложной, но необходимой логики спрятали за двумя несложными для понимания ключевыми словами.
>пользователь C# сам эту сложность соорудить не в состоянии
Он в состоянии соорудить практически то же самое, что только отсутствием синтаксического сахара будет отличаться от того как щас сделано.
Просто по объективным причинам объём работы большой.
>ничего общего не имеет с элегантностью языка. Практично? Да. Удобно? Да.
Именно это я и называю элегантностью.
Язык программирования — это не произведение искусства, а инструмент для решения реальных задач.
>В C# тоже с этим не всё так хорошо как в лиспе, например.
Лисп даже не умеет традиционного статически типизированного ООП.
no subject
Date: 2013-05-07 11:52 pm (UTC)А ведь когда-то была многообещающая платформа. Но дальше каждый сам делает свои выводы.
no subject
Date: 2013-05-08 01:28 am (UTC)"A -> Type" определяет тип функций, которые для любого х из А нам вернут какой-то тип.
Пи-тип "(a:A) -> B a" (где В : A -> Type) определяет тип функций, которые для любого х из А вернут одно значение из того типа, который вернула В.
Первое для каждого х возвращает целое пространство, второе лишь точку в этом пространстве. Расслоение и сечение.
А какого типа Type?
Date: 2013-05-08 05:10 am (UTC)Привыкли, понимаешь, к идрисам всяким... ;-)
no subject
Date: 2013-05-08 05:10 am (UTC)no subject
Date: 2013-05-08 05:17 am (UTC)Где фильтрации — не увидел.
Торможу или "не то" имелось в виду?
no subject
Date: 2013-05-08 05:31 am (UTC)По этим обычно понимают что-то пролого-подобное, которое динамическое, а значит, без ущерба для выразительности, может быть использовано в качестве библиотеки.
Правда, если такое есть compile-time, это уже серьёзнее, но вряд ли обычно это имеют в виду.
no subject
Date: 2013-05-08 05:35 am (UTC)АмстердаСтокгольмский синдром. :)Re: А какого типа Type?
Date: 2013-05-08 05:38 am (UTC)Re: А какого типа Type?
Date: 2013-05-08 05:44 am (UTC)Думал, оно импредикативное. Забавно, спасибо.
no subject
Date: 2013-05-08 06:35 am (UTC)Не навязывает стиля программирования. Предлагаемый инструментарий настолько широк, что я вообще никуда с него не хотел бы слазить.
Основная парадигма - да, ООП, но если нужно - вот вам линк, если мало - вот вам вся та же мощная инфраструктура, но обернутая в полностью функциональный и готовый для продакшына F#. И еще есть же mono!
Мне кажется, что C# напрасно обделяют вниманием.
Re: А какого типа Type?
Date: 2013-05-08 07:28 am (UTC)no subject
Date: 2013-05-08 07:38 am (UTC)Язык программирования — это не произведение искусства, а инструмент для решения реальных задач. — с этого я и начинал. Но тем самым вы ограничиваете себя именно кругом «реальных задач», это и есть «зона комфорта» C#, в то время, как более развитые языки (H. и L.) обладают существенной гибкостью, которая позволяет им неплохо себя чувствовать in the wild, в поле, в котором еще никто подобных задач не решал (и откуда и приходят все фичи, которыми так гордится сообщество C#, похоже, не подозревая о существовании хотя бы Microsoft Research, словно их дают незримые майкрософтовские боги — именно это подразумевалось под «пользователь C# сам эту сложность соорудить не в состоянии», у него есть возможность, потенциальность, но откуда возьмутся идеи?). Вы и не подозреваете, сколько прекрасных идей и инструментов все еще нет в C# и не будет.
Лисп даже не умеет традиционного статически типизированного ООП. — Clojure очень даже умеет, поскольку она работает поверх JVM, вот только сами кложуристы избегают тыкать палочкой в goop без особой необходимости. Думаю, соорудить Java-style велосипед вполне возможно и в Common Lisp, но зачем?!
Re: А какого типа Type?
Date: 2013-05-08 07:58 am (UTC)Это способ сделать систему предикативной (не определять понятия через самих себ), но оставив гибкость.
no subject
Date: 2013-05-08 08:19 am (UTC)В пехепе выразительность языка согласована с возможностями рантайма и глубиной системы типов. Вот ю си из вот ю гет, всё по честному.
Вон на питоне или жаваскрипте в принципе можно сделать стойку на ушах навроде хламидомонадического комбинатора, но это будет неподдерживаемый ФАРШЪ.