?

Log in

Miguel

Recent Entries

You are viewing the most recent 50 entries.

25th July 2015

23:11: Не прошло и года
...и я таки понял, что был неправ.

Конкретно это вот про этот пост: http://migmit.livejournal.com/55397.html

То есть, нет, частично я был прав — я попробовал iPhone 6 (без плюса) в магазине и понял, что пользоваться им не могу. Но вот по поводу часов я таки ошибался.

Да-да, я взял себе Apple Watch. Стальные, с миланским ремешком. И должен сказать, что ремешок этот значительно приятнее тех, с которыми я сталкивался раньше. Достаточно уже того, что его можно точно подогнать по размеру.

Модель взял маленькую — у меня тонкие кисти, так что большая смотрелась на руке странно (да, я померил обе).

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

Пошаговая навигация прекрасна. Вы задаёте — на айфоне — конечную точку, после чего идёте по маршруту, не глядя ни на часы, ни на телефон. Когда надо повернуть — часы похлопают вас по руке, а, если вы поднимете руку, то увидите, куда повернуть. Потрясающе.

Как ни странно, диктовка работает хорошо. Когда, например, меня упоминают в Trello, мне приходит на часы уведомление; при этом я могу прямо с часов надиктовать короткий ответ. Забавно, что при этом знаки препинания надо проговаривать, типа "oh comma cool exclamation point".

1Password умеет часть паролей засовывать на часы. То есть, если мне нужно, например, посмотреть номер своей карты, я могу просто запустить приложение на часах.

Батарейка живёт день. Мне вполне достаточно — ну, ещё один девайс нуждается в зарядке, большая разница. Спать в часах я всё равно не собираюсь.

Посмотрим, что принесёт watchOS 2.

2nd July 2015

20:57: Происходящее
Так, чтобы зафиксировать, что сейчас происходит.

Кто сказал "Путин"? Тьфу на вас. Не о том речь.

В общем, я сейчас работаю в компании Kinja, которая, по сути, является Будапештским офисом компании Gawker. Да-да, той самой, которая публикует всякие слухи и сплетни, называя это дело "новостями".

И есть такой товарищ по имени "Халк Хоган" (нет, нифига не настоящее). Я лично его знаю только по (убогому) сериалу "Гром в раю", который когда-то крутили по ящику и я на него иногда попадал (в то время, когда ящик был ещё не "зомбо-").

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

Подчеркну, что эту запись сделали не мы. Мы также её не украли, не заплатили кому-нибудь, чтобы он украл её для нас, и так далее. Тем не менее, Х требует от нас (теперь уже нас) 100 миллионов долларов.

У нас столько нет.

Дело будет слушаться во граде Санкт-Петербурге. !. Правда, в том, который во Флориде. Насколько я могу судить, наша основная защита — первая поправка (предупреждаю: я не юрист). Если мы проиграем, то, вероятно, контора всё-таки не закроется, но поменяет владельцев.

На текущий момент слушанье, назначенное изначально на 6 июля, перенесено на неопределённый (пока) срок — там какая-то бюрократия, я не понял точно.

16th June 2015

14:45: Доказательное, часть II
Продолжаем наши игры. Мы переходим к более интересной части: теперь мы будем не только следить за длиной списка, но и позаботимся, чтобы в конце список оказался отсортированным.

Как можно отслеживать подобное при компиляции? Мы знаем, что на этапе компиляции мы можем следить только за типами; все значения одного типа на этом этапе неотличимы. Никаких зависимых типов тут нет. Так как же?

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

Сделать это всё, конечно, можно. А вот сделать это полиморфным образом — чтобы не требовать от типа ничего, кроме упорядоченности — уже нельзя. Никак. Да и вообще, это весьма сомнительный трюк. Как же тогда быть?

На помощь приходят уникальные типы. Да-да, в Хаскеле есть уникальные типы — то есть, такие типы, что в программе возможно только одно значение этого типа (ну, до тех пор, пока мы не начали использовать unsafeCoerce и тому подобные вещи). Да, все значения одного типа неотличимы — но если у типа только одно значение, то они к тому же и в рантайме будут гарантированно равны. Итак, как получить уникальный тип в Хаскеле?
> {-# LANGUAGE GADTs #-}
> module Tag(Tag(untag), Tagged(Tagged), tag) where
Наш уникальный тип — это, по сути, тот же тип, что и раньше, только с навешенным на него "тэгом" — фантомным типом.
> newtype Tag t a = Tag {untag :: a}
Что же делает его уникальным? То, что конструктор Tag мы не экспортируем. Только сам тип и функцию untag. Поэтому, если мы каким-то образом обеспечим возможность создать значение данного типа, но только одно — то наша задача выполнена. Все значения такого типа, созданные вне данного модуля, будут равны.

Чтобы это сделать, мы воспользуемся всё теми же GADT-ами. Именно, мы спрячем этот самый "тэг" под экзистеншиал:
> data Tagged a where Tagged :: Tag t a -> Tagged a
Теперь мы не будем создавать сам Tag t a, мы будем создавать Tagged a:
> tag :: a -> Tagged a
> tag a = Tagged (Tag a)
Таким образом, мы можем создать кучу значений типа Tagged a — но, распаковав их с помощью паттерн-матчинга, мы получим значения типов Tag t a с разными t. То есть, мы можем создать кучу "тэгированных" значений, несущих внутри себя одно и то же a — но сами по себе они будут разных типов. Мы даже не сможем сравнить их на равенство: равенство действует только в пределах одного типа.

И как же это поможет нам со сравнениями? А вот как. Мы не можем сделать тип 3 < 5 — так как 3 и 5 это значения, а не типы. Но если есть уникальный тип, содержаший значение 3 с тэгом t, и уникальный тип, содержащий значение 5 с тэгом s, мы вполне можем соорудить тип t < s.

Однако, можно также образовать и тип s < t. Как мы гарантируем, что это не происходит? Точно так же, как и в случае тэгов: экспортируя не конструктор, а функцию, проверяющую неравенство.
> {-# LANGUAGE GADTs, TypeOperators #-}
> module Ord(
>   (:=), refl, symm, eTrans,
>   (:<), (:>), mirrorLG, mirrorGL, lTrans, gTrans,
>   Comparison(..), comparison
>   ) where
> import Tag
Но для начала, прежде чем заняться неравенствами, мы займёмся равенствами.
> data t := s = Equal
Напомню, что мы сравниваем тэги, а не сами значения.

Равенство будет, как обычно, рефлексивными, симметричным, и транзитивным. Ничего из этого нам не понадобится, но для полноты картины пусть будет. Кстати, рефлексивность можно интерпретировать как "если есть два значения с одним тэгом, то они равны".
> refl :: t := t
> refl = Equal

> symm :: t := s -> s := t
> symm Equal = Equal

> eTrans :: t := s -> s := r -> t := r
> eTrans Equal Equal = Equal
Переходим к неравенствам. Достаточно было бы, конечно, ввести одно из них, но я введу оба, и "больше", и "меньше". С симметричными вещами проще работать.
> data t :< s = Less

> data t :> s = Greater
Заметьте, что конструкторы Less и Greater мы не экспортируем, чтобы не допустить прямого создания этих типов вне данного модуля.

Разумеется, эти отношения эквивалентны с точностью до перестановки аргументов:
> mirrorLG :: t :< s -> s :> t
> mirrorLG Less = Greater

> mirrorGL :: t :> s -> s :< t
> mirrorGL Greater = Less
Оба они транзитивны:
> lTrans :: t :< s -> s :< r -> t :< r
> lTrans Less Less = Less

> gTrans :: t :> s -> s :> r -> t :> r
> gTrans Greater Greater = Greater
Остаётся показать, как именно мы будем создавать их. Когда мы сравниваем два значения, может получиться три варианта ответа: первое больше второго, первое равно второму, первое меньше второго. Соответственно, мы введём тип для такого ответа:
> data Comparison t s = CL (t :< s) | CE (t := s) | CG (t :> s)
И, наконец, напишем функцию, которая это сравнение производит.
> comparison :: Ord a => Tag t a -> Tag s a -> Comparison t s
> comparison ta sa =
>   case compare (untag ta) (untag sa) of
>    LT -> CL Less
>    EQ -> CE Equal
>    GT -> CG Greater
Отмечу одно обстоятельство. Мы используем здесь класс Ord, инстанс которого может быть написан неправильно. Например, сравнение может быть нетранзитивным. В этом случае, используя функцию lTrans, мы можем, в принципе, выразить в типах неверное сравнение. Это никак не обойти: транзитивность сравнений нам действительно будет нужна. Но, с другой стороны, если сравнение реализовано неправильно, то и QuickSort будет работать неправильно.

Итак, вся машинерия готова. Теперь со всем этим мы попробуем взлететь.
> {-# LANGUAGE GADTs, TypeOperators #-}
> module QuickSort where
> import Num
> import Ord
> import Seq
> import Tag
Вспомним ещё раз, как делается QuickSort. Для начала мы делим наш список на два, в одном — маленькие элементы, в другом — большие. При этом на сей раз за такими ограничениями мы должны следить. Значит, простой Seq нам не подойдёт, нужен другой тип, который, во-первых, имеет эти самые ограничения сверху и снизу, а, во-вторых, содержит значения вместе с тэгами, без которых мы не можем отслеживать порядок.
> data BoundedSeq l u n a where
>   ZBSeq :: BoundedSeq l u Zero a
>   SBSeq :: Tag t a -> l t -> u t -> BoundedSeq l u n a -> BoundedSeq l u (Succ n) a
То есть, мы храним не только значения с тэгами, но и свидетельства того, что эти значения удовлетворяют наложенным ограничениям.

Правда, изначально никаких ограничений нет. Поэтому нам понадобится "пустое" ограничение:
> data Whatever a = Whatever
Ну и, чтобы старый Seq переделать в новый BoundedSeq, нам понадобится простенькая функция:
> fromSeq :: Seq n a -> BoundedSeq Whatever Whatever n a
> fromSeq ZSeq = ZBSeq
> fromSeq (SSeq a as) = case tag a of Tagged ta -> SBSeq ta Whatever Whatever (fromSeq as)
Далее, в списке могут случиться повторяющиеся элементы. То есть, одно из ограничений — сверху или снизу — должно быть нестрогим.
> data t :<= s = L (t :< s) | E (t := s)
Идём дальше. Что мы собираемся получить на этом первом этапе? В предыдущем варианте у нас был тип TwoSeq, специально придуманный ради этого. Не будем нарушать традиции. Единственное, что нужно добавить — это, опять-таки, ограничения сверху и снизу.
> data TwoSeq l u n t a where
>   TwoSeq
>     :: BoundedSeq l ((:>) t) m a
>     -> BoundedSeq ((:<=) t) u k a
>     -> Plus m k n
>     -> TwoSeq l u n t a
К сожалению, инфиксные операторы на уровне типов не позволяют делать секции, иначе можно было бы писать не ((:>) t), а просто (t :>).

Функция, разделяющая список на два, выглядит почти так же, как и в предыдущем случае:
> split :: Ord a => Tag t a -> BoundedSeq l u n a -> TwoSeq l u n t a
> split _ ZBSeq = TwoSeq ZBSeq ZBSeq ZPlus
> split ta (SBSeq ta' lt ut as) =
>   case split ta as of
>    TwoSeq sl sg p ->
>      case comparison ta ta' of
>       CL l -> TwoSeq sl (SBSeq ta' (L l) ut sg) (sPlus p)
>       CE e -> TwoSeq sl (SBSeq ta' (E e) ut sg) (sPlus p)
>       CG g -> TwoSeq (SBSeq ta' lt g sl) sg (SPlus p)
Единственное, о чём нужно позаботиться — это о том, чтобы аккуратно свести случаи "меньше" и "равно".

Теперь второй этап. Мы хотим получить на выходе отсортированную последовательность. Значит, нам потребуется для этого отдельный тип.
> data SortedSeq l u n a where
>   ZSSeq :: SortedSeq l u Zero a
>   SSSeq :: Tag t a -> l t -> u t -> SortedSeq ((:<=) t) u n a -> SortedSeq l u (Succ n) a
Ну и, для преобразования отсортированной последовательности в обычную, простенькая функция:
> toSeq :: SortedSeq l u n a -> Seq n a
> toSeq ZSSeq = ZSeq
> toSeq (SSSeq ta _ _ as) = SSeq (untag ta) (toSeq as)
Теперь — о функции слияния. Тут есть один занятный момент.

Мы склеиваем две последовательности. Отсортированных. Но у первой из них верхняя граница — это выбранный pivot, а не та граница, которая была у исходной, неотсортированной последовательности. Значит, нам нужно как-то скастовать одну последовательность к другой, ослабив ограничение. Для этого нам послужит такой класс:
> class Lower p where lower :: p t -> t :> s -> p s
> instance Lower Whatever where lower Whatever _ = Whatever
> instance Lower ((:>) r) where lower = gTrans
То есть, мы можем либо ослабить ограничение до пустого (Whatever), либо до какой-то большей, но всё-таки существующей, границы.

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

И вот какая функция получается. По чисто техническим причинам мне удобнее склеивать не две последовательности сами по себе, а две последовательности и pivot между ними.
> append3
>   :: Lower u => SortedSeq l ((:>) t) m a
>   -> Tag t a
>   -> l t
>   -> u t
>   -> SortedSeq ((:<=) t) u k a
>   -> Plus m (Succ k) n
>   -> SortedSeq l u n a
> append3 ZSSeq ta lt ut as2 p = uzPlus p (SSSeq ta lt ut as2)
> append3 (SSSeq ta' lt' ut' as1) ta _ ut as2 p = append3Succ ta' lt' ut' as1 ta ut as2 p

> append3Succ
>   :: Lower u => Tag t' a -> l t' -> t :> t'
>   -> SortedSeq ((:<=) t') ((:>) t) m a
>   -> Tag t a -> u t
>   -> SortedSeq ((:<=) t) u k a
>   -> Plus (Succ m) (Succ k) n
>   -> SortedSeq l u n a
> append3Succ ta' lt' tt' as1 ta ut as2 (SPlus p) =
>   SSSeq ta' lt' (lower ut tt') (append3 as1 ta (L (mirrorGL tt')) ut as2 p)
Причина появления дополнительной функции append3Succ — прежняя, иначе ghc будет ошибочно жаловаться на неполный паттерн-матчинг.

Наконец, собираем всё вместе.
> sort :: (Lower u, Ord a) => BoundedSeq l u n a -> SortedSeq l u n a
> sort ZBSeq = ZSSeq
> sort (SBSeq ta lt ut as) =
>   case split ta as of TwoSeq sl sg p -> append3 (sort sl) ta lt ut (sort sg) (sPlus p)
Что хотели — то и получили: из обычной последовательности делается отсортированная. Заметим, что тут уже не получится забыть рекурсивно отсортировать подсписки — компилятор тут же выдаст ошибку типизации.

Ну и, возвращаясь к обычным последовательностям, без ограничений, мы получаем функцию
> sortSeq :: Ord a => Seq n a -> SortedSeq Whatever Whatever n a
> sortSeq as = sort (fromSeq as)
Чтобы протестировать её, введём дополнительную функцию, сортирующую уже обычный, хаскельный список:
> sortList :: Ord a => [a] -> [a]
> sortList as = case fromList as of ESeq as' -> toList (toSeq (sortSeq as'))
Тестируем:
*QuickSort> sortList [5,1,3,2,4,1] :: [Integer]
[1,1,2,3,4,5]
14:44: Доказательное, часть I
Дошло тут до меня о великий царь, что, хотя в Haskell и нет зависимых типов, но его способности к доказательству на самом деле очень и очень велики.

Если говорить, например, об алгоритмах сортировки, то, обычно, приходит в голову две очевидных вещи:
1) Результат должен быть отсортирован, и
2) Результат должен иметь ту же длину, что и исходная последовательность.

Так вот, я раньше думал, что пункт (2) можно отследить типами, а вот пункт (1) — ни фига. Но оказывается...

Прежде, чем писать о пункте (1), я, всё-таки, займусь пунктом (2). Он, конечно, почти очевиден — во всяком случае, очевидно, что это МОЖНО сделать, — но, глядя, как он делается, мы можем лучше понять более сложный пункт (1).

Итак. Само собой, чтобы доказывать на Haskell выполнение пункта (2), нам нужно завести числа на уровне типов. Да, сейчас есть всякие promoted literals и всё такое прочее... но мы будем работать по простому. По сути, единственное расширение, которое нам понадобится по делу — это GADT-ы:
> {-# LANGUAGE GADTs #-}
Ну, для начала мы таки введём наши числа:
> module Num where

> data Zero = Zero
> data Succ n = Succ n
Один момент: почему тип Succ определён именно как data, а не newtype? Дело в том, что паттерн-матчинг для GADT-ов сделан не самым лучшим образом, и, зачастую, сыплет warning-ами на совершенно правильный и законный код. В частности, мы поймаем их, если используем здесь newtype. Этот баг известен; сначала его исправление назначили на версию 7.8.1, потом сдвинули на 7.10.1, потом — на 7.12.1, где он сейчас и висит.

Мы будем доказывать банальный QuickSort. И довольно простой, и довольно эффективный. Но, коли так, нам нужно будет соединять две последовательности в одну. А, значит, их длины — за которыми мы будем следить — складываются. Стало быть, нам нужно сложение чисел.

Операции над типами сейчас модно делать с помощью type families. Но мы, опять-таки, поработаем по старинке, чтобы не уйти в дебри undecidable instance-ов.

А по старинке — это те же GADT-ы:
> data Plus m k n where
>     ZPlus :: Plus Zero n n
>     SPlus :: Plus m k n -> Plus (Succ m) k (Succ n)
Здесь значение типа Plus m k n — это свидетельство того, что m+k=n. Ясен пень, что такое свидетельство можно подделать — например, явно указав в качестве него undefined — но всё-таки более-менее полагаться на него можно. Опять-таки, полноценных доказательств тут не будет, в Haskell каждый тип населён — но работать они помогают.

Нам понадобятся два факта про сложение. Во-первых, хотя из определения мы знаем, что, увеличивая первое слагаемое на единицу, мы увеличиваем и всю сумму, нам пока неизвестно то же самое про второе слагаемое. Вот и установим этот факт:
> sPlus :: Plus a b c -> Plus a (Succ b) (Succ c)
> sPlus ZPlus = ZPlus
> sPlus (SPlus p) = SPlus (sPlus p)
А второе, что нам потребуется — довольно технический факт: если f — произвольный тип (подходящего кайнда), то f (0 + n) — это то же самое, что f n. Практически тривиально:
> uzPlus :: Plus Zero k n -> f k a -> f n a
> uzPlus ZPlus fna = fna
Здесь я добавил ещё один аргумент a — по чисто техническим причинам; так будет удобнее впоследствии. Можно было и обойтись.

На этом с числами всё. Займёмся теперь последовательностями.
> {-# LANGUAGE GADTs #-}
> module Seq where
> import Num
Так как мы будем отслеживать длины последовательностей, нам нужно внести длину в, собственно, тип. Числа у нас уже есть, так что всё остальное просто:
> data Seq n a where
>     ZSeq :: Seq Zero a
>     SSeq :: a -> Seq n a -> Seq (Succ n) a
Чтобы иметь возможность посмотреть содержимое такой последовательности, сконвертируем её в обычный список:
> toList :: Seq n a -> [a]
> toList ZSeq = []
> toList (SSeq a as) = a : toList as
Обратный процесс — преобразование списка в такую последовательность — чуть более сложен. В списке не содержится его длина — так что на выходе должна быть последовательность произвольной длины, что означает дополнительный тип:
> data ESeq a where ESeq :: Seq n a -> ESeq a

> fromList :: [a] -> ESeq a
> fromList [] = ESeq ZSeq
> fromList (a : as) = case fromList as of ESeq as' -> ESeq (SSeq a as')
Добрались до, собственно, сортировки.
> {-# LANGUAGE GADTs #-}
> module QS where
> import Num
> import Seq
Нам надо будет разделить наш список на две части, при этом следя за их длинами. А что мы будем знать про их длины? Да то, что их сумма — это длина исходного списка. То есть, на выходе нашей "делительной" функции должно оказаться что-то вот такое:
> data TwoSeq n a where TwoSeq :: Seq m a -> Seq k a -> Plus m k n -> TwoSeq n a
Сама функция пишется проще простого: делим хвост списка, после чего добавляем первый его элемент куда нужно. Чтобы решить, куда именно — сравниваем этот самый первый элемент с выбранным зарание pivot-ом. Я специально не стану обобщать и делать деление списка по произвольному предикату, чтобы было более похоже на то, что будет происходит в следующем посте, про пункт (1).
> split :: Ord a => a -> Seq n a -> TwoSeq n a
> split _ ZSeq = TwoSeq ZSeq ZSeq ZPlus
> split a (SSeq a' as) =
>     case split a as of
>       TwoSeq sl sg p ->
>           if a' < a
>             then TwoSeq (SSeq a' sl) sg (SPlus p)
>             else TwoSeq sl (SSeq a' sg) (sPlus p)
Кроме того, такие последовательности придётся склеивать обратно. Для этого нам понадобится аналогичная функция:
> append :: Seq m a -> Seq k a -> Plus m k n -> Seq n a
> append ZSeq as2 p = uzPlus p as2
> append (SSeq a as1) as2 p = appendSucc a as1 as2 p

> appendSucc :: a -> Seq m a -> Seq k a -> Plus (Succ m) k n -> Seq n a
> appendSucc a as1 as2 (SPlus p) = SSeq a (append as1 as2 p)
Зачем понадобилась дополнительная функция appendSucc? Как и выше — чтобы избежать ошибочных warning-ов ghc по поводу "неполного паттерн-матчинга". Увы, ghc в этом месте довольно глуп, и не понимает, что, на самом деле, все остальные варианты паттерн-матчинга невозможны. Причём если их добавить, то он выдаст ошибку типизации (что правильно).

Заметим, кроме того, что в одном месте нам понадобилось использовать функцию uzPlus. По сути, вот из-за чего: мы знаем, что длина получающейся последовательности равна m+k и что n=m+k; но из этого не можем — автоматически — сделать вывод, что длина получающейся последовательности равна n. Приходится кое-где скастовать явно.

Ну и, наконец, вот она, сортировка.
> sort :: Ord a => Seq n a -> Seq n a
> sort ZSeq = ZSeq
> sort (SSeq a as) =
>     case split a as of TwoSeq sl sg p -> append (sort sl) (SSeq a (sort sg)) (sPlus p)
То, что и хотелось — длина списка на выходе гарантированно совпадает с длиной списка на входе. Алгоритм работает тривиально, пустой список выдаётся как есть, остальное делится на два подсписка, они сортируются отдельно, после чего соединяются (с pivot-ом посередине).

Здесь я должен признаться в одном упущении. Когда я писал этот алгоритм в первый раз, я забыл рекурсивно отсортировать подсписки. Получилось что-то в таком роде:
sort (SSeq a as) = case split a as of TwoSeq sl sg p -> append sl (SSeq a sg) (sPlus p)
Соответственно, результат получился хоть и нужной длины, но слегка не такой, как хотелось бы. Вот этим мы в следующем посте и займёмся.

Напоследок — функция для проверки всего этого добра:
> sortList :: Ord a => [a] -> [a]
> sortList as = case fromList as of ESeq as' -> toList (sort as')
Проверяем:
*QS> sortList [5,1,3,2,4] :: [Integer]
[1,2,3,4,5]

6th June 2015

21:45: Рифмованое
Из обращения президента Порошенко к Верховной Раде:
Найсвіжіший приклад правильного рішення: днями Мінекономіки виключило із списку обов'язкових до сертифікації на території України цілу низку імпортних товарів – від автомобілів і тракторів до дитячих візків і порцелянового посуду. Правду кажучи, сертифікації не було й раніше, просто брали хабарі. А тепер – брати не будуть: і хотіли б, а нема за що.
Из газеты Слава Севастополя:
С 1 июня 2015 года триста пятьдесят балаклавских и севастопольских предпринимателей, оплативших патенты за морские пассажироперевозки в бюджет Севастополя, регулярно с января пополняющих счета Пенсионного фонда, законопослушно оплативших услуги проверки своих катеров специалистами Российского речного регистра и Севастопольского морского порта для перерегистрации своих маломерных судов как коммерческих, работать... не будут.
...
на 26 мая 2015 года только одна компания Севастополя, ООО "Севастопольские транспортные системы", эксплуатирующая паромы по маршруту Артиллерийская бухта - Северная сторона, имеет с 1 января 2015 года государственную лицензию МР-2 N 001572 от 30 декабря 2014 года с правом осуществления морских пассажироперевозок.
Даже Севастопольский морской порт, владелец многочисленных катеров, перевозящих пассажиров на Северную сторону и Радиогорку, так и не смог на сегодняшний день получить эту вожделенную лицензию.

27th May 2015

12:18: Башкирское
Мне тут пришло в голову — а ведь, наверное, не все ещё видели этот башкирский мультик:

Это большое упущение.

26th May 2015

01:42: Извинения
Фотография со страусом, выложенная позавчера, принадлежит sonya_oreshnik. Приношу свои глубочайшие извинения, что не указал авторство сразу. Я присутствовал при её создании, но не создавал её сам. Ещё раз прошу простить.

25th May 2015

22:19: Роличное
Снова пишу пост для обозначения позиции.

ИМХО, когда Чулпан Хаматова публично говорит несколько слов в поддержку ВВБХ — при всей его мерзотности — в обмен получая возможность спасти десяток детей, она делает единственно достойный выбор.

Я сказал.

22nd May 2015

13:35: Verisimilitude
1. Вероятность, правдоподобие
2. Выдумка

Обожаю английский.

20th May 2015

22:13: Техподдержечное
У нас на работе в рассылку падают, в том числе, обращения в нашу техподдержку. Читать их порой забавно. На днях пришло вот такое вот (перевёл в меру сил):
все американские актёры и остальные тоже в огромном криминальном скандале, в котором участвуют миллиарды очень важных персон ресторан мадео в беверли хиллз калифорния и прочие , такие как дэвид хассельхоф мел гибсон джоли хопкинс мишель пфайфер джекниколсон и миллиарды других они вовлечены в огромный скандал из за которого все их знакомые и начальники работают над пулей в мою голову и правительство сша тоже . я хранитель печати 50 штатов сша и монарх совращающий миллиарды людей. я карен кимберли и джон маккейн сенатор от аризоны и мой единственный господин муж гарри чарльз дэвид второй сын чарльза и дианы спенсер . мы трое очень важные сегодня люди я также стану единственным федеральным правительственным монархом и единственным послом мира .смертная казнь всем американским актёрам пусть они умрут в тюрьме со всей своей семьёй.
Не могу представить, какой выдержкой нужно обладать, чтобы написать в ответ:
Если у вас есть вопрос или совет редакторам вашего любимого блога, пожалуйста, отправьте письмо в по соответствующему адресу:
(список наших основных блогов и электронных адресов)

15th May 2015

14:49: Чайное
Почему-то здесь, в Будапеште, чайные пакетики завариваются практически мгновенно, в то время как в Питере приходится подолгу ждать, размешивать, отжимать пакетик ложкой, пока не добьёшься приемлемого эффекта.

Упаковка чайных пакетиков, отправленная из Будапешта в Питер, не поменяла расклад: заваривается по-прежнему плохо. Может, это от качества воды зависит? Вода, естественно, кипячёная.

14th May 2015

19:22: Конференционное
29 мая я буду на конференции Joy of Coding в Роттердаме. Программа выглядит очень интересно.

13th May 2015

21:40: Ватничное
У меня есть такая нехорошая черта — склонность поддерживать начальство. Если, конечно, оно даёт к этому хоть какую-то возможность.

Да-да, правда-правда. Как сейчас помню, когда ВВБХ сделал Михалкова-старшего трижды гимнюком, я довольно долго доказывал своим родным и близким, что, дескать, ничего страшного, что оно правильно всё делает, что нам так пофиг, а старичкам, может, приятно. Ну, и так далее.

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

Так вот. В сообществе pora_valit очередная драма, один гонит на модераторов, я, как обычно, наоборот, в общем, шум, гам, веселуха, и проч. И вот на первый же мой коммент человек отвечает так:

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

12th May 2015

21:14: Парадное
Что-то меня совершенно не заботят те люди, которые вышли в этом "бессмертном полку" с чужими (или не чужими) фотографиями за деньги или из-под палки, а потом побросали эти фотографии в мусорку. Деньги — вещь нужная, фотографии на палках — нет, сама акция бессмысленна изначально.

Меня больше удивляют те, кто пошёл с фотографиями своих предков и по зову сердца. Как это так — гордиться тем, что сделал не ты? Я ещё понимаю, гордиться успехами своего ребёнка — всё-таки, ты его воспитывал, обучал, да, в конце концов, именно ты выбрал второго родителя. Но предка? Это предок должен твоими успехами гордиться, а не наоборот.

On an unrelated note: Гугль снова проснулся и захотел меня поинтервьюить ещё. Вежливо, но кратко ответил, что в данный момент не заинтересован.

11th May 2015

09:38: Я не опоздал?
А то никак руки не дойдут записать прогноз на следующий ДП, а прошлое меняется быстрее, чем скала проект компилирует.

Да, прогноз: эрэфия будет праздновать совместную с Рейхом победу над фашистской Америкой.

9th May 2015

01:33: Вуайеристское
Парой постов avva навеяло.

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

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

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

Аргумент "не хотел смотреть — мог отвернуться" является при этом типичнейшим проявлением blame the victim. Соблюдение прав человека является — по умолчанию — обязательным, даже если человек не делает ничего, чтобы свои права отстоять. Грабитель является преступником, даже если его жертва не сопротивлялась. Насильник является преступником, даже если его жертва шла в мини-юбке ночью по тёмному переулку. Другое дело — если жертва сама приложила усилия к тому, чтобы стать жертвой. Человек, обманом проникший в закрытый клуб, не имеет никакого права высказывать претензии к происходящему на сцене.

Это я всё к той парочке, которая занялась сексом на пляже во Флориде. Соответственно, ей теперь грозит пара месяцев тюрьмы (ИМХО — очень мало), ему — 15 лет, так как он раньше попадался на транспортировке наркоты; кроме того, оба попадают в sex offenders list, что очень сильно усложняет им жизнь (и это хорошо).

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

5th May 2015

12:10: Хотите верьте, хотите — нет,
но этот пост делается исключительно по рабочей надобности.

15th April 2015

22:53: Беспроводное
Ещё из современных штук, от которых все тащатся, а я не понимаю. Вот беспроводная зарядка — она зачем?

Если я правильно понимаю, то беспроводная зарядка — это такая подставка, на которую надо положить телефон, после чего он начинает заряжаться. Это так?

То есть, когда я вставляю провод, я могу положить телефон куда угодно в пределах длины провода. Что более существенно, я могу взять его в руки и с ним работать. Могу позвонить кому-то. И при этом телефон будет исправно продолжать заряжаться.

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

Так нафига эта беспроводная зарядка нужна?

31st March 2015

00:49: Общее место или нет?
Есть такой треугольник из имён. Я его в своё время обнаружил сам, но, может, он всем известен:

1) Денис Давыдов. Полковник, командир партизанского отряда в войне 1812 года (и, к тому же, поэт).

2) Василий Денисов. Полковник, командир партизанского отряда в "Войне и мире" Толстого.

3) Давыд Васильев. Полковник, командир партизанского отряда в пьесе "Давным-давно" (возможно) Гладкова и фильме "Гусарская баллада" Рязанова.

Васька Денисов стихов, вроде, не писал — зато обалденно танцевал мазурку. А вот Давыд Васильев — почти писал:
Ну вот, уснул бивак наш партизанский.
Люблю картину эту, хоть пестра.
Овса кули, соломы жёлтой связки,
Ночное небо, зарево костра,
Усы, торчащие подобно острым пикам,
И храп, что пострашнее львиных рыков
И посильней архангельской трубы.
Поэзия есть в эдаком пейзаже.
             Р ж е в с к и й
Поэзия? Ну, вот ещё что скажешь!
             В а с и л ь е в
Да, брат, поэзия! Найдётся, может быть,
И среди нас достойный сочинитель,
Опишет это всё, когда пройдёт война,
И наша боевая старина
Воскреснет в сказках, что читают детям.
Current Mood:

26th March 2015

22:41: Как всегда
вариантов оказывается немного: то ли они все там анаши перекурили, то ли... на этом мысль останавливается.
Current Mood:

17th March 2015

13:31: Получил справку
что не идиот. Ещё одну. Mensa HungarIQa — как я понимаю, местное отделение Mensa International — прислала мне уведомление, что у меня IQ выше, чем у 99% людей на Земле и они готовы со всей нежностью принять меня в свои ряды.

Интересно, кроме строчки в резюме и личного удовлетворения, какие-нибудь бенефиты от этого будут?
Current Mood:

13th March 2015

12:59: Чувствую себя устаревшим.
Столько всего происходит, столько всего появляется, что у всех на устах и в компьютерах — а я понять не могу, нафига всё это надо. И не то, чтобы я не пробовал — я именно пробую... и всё равно не понимаю.

Facebook. Дофига и более народу им пользуется. У меня тоже есть Facebook (и даже два — долгая история). Я туда не хожу. Я не знаю, куда там ходить. Я не знаю, что там искать. И как искать (интерфейс у них откровенно ублюдочный). Да, я как-то попользовался ихним чатом. И, в общем-то, всё. Если мне надо что-то написать — я пишу сюда, в ЖЖ. Здесь по крайней мере есть нормальные древовидные комменты. Поставить лайк? А зачем?

Evernote. Тоже: куча народу пользуется и восхищается, как это круто. Я попробовал. Ну нифига ж не круто. Ну да, можно туда чой-то запихнуть. Даже довольно многое. А зачем? Шоб було?

IFTTT. Идея выглядит очень круто: автоматизировать работу с телефоном — в ограниченных масштабах, но это-то и хорошо, а то люди начнут видеорендеринг на этом деле писать. Ну что ж, поставил. ОЧЕНЬ долго пытался придумать ХОТЬ ЧТО-НИБУДЬ, что эта штука может, чтобы мне это было нужно. Не придумал. Всё, что предлагают — это штуки типа "если вы лайкнули что-то в фейсбуке — написать об этом в твиттер". НАХРЕНА???

Смарт-часы. Я благословляю того человека, который догадался, что мобильный телефон должен показывать время. Потому что это позволило мне наконец избавиться от этой тяжёлой, неуклюжей, безумно лишней штуки на запястье. Впрочем, не совсем: некоторое время я носил часы на цепочке. Это было ещё более-менее. А тут Тим Кук со сцены объявляет: я, дескать, с детства мечтал отвечать на телефонные звонки с часов. Я как представлю, как надо руку вывернуть, чтобы запястье оказалось рядом с ухом...

Siri и прочее голосовое управление. Ну, соглашусь: в мороз может быть проще сказать в микрофон "Набрать: дом", чтобы перчатки не снимать. И то, по-моему ещё лучше завести на случай холодов стилус. Но это максимум! Ведь настолько проще открыть "календарь" и создать новое событие, чем произносить "рабочее совещание завтра в пятнадцать-тридцать". Особенно учитывая, что после этого всё равно понадобится открывать "календарь" и редактировать событие, чтобы дописать туда напоминание за пять минут.

В общем, да. Консерватор я. Совершеннейший.

10th March 2015

17:45: Наконец нашёл время поискать ответ...
...и нашёл.

Функция x\sqrt{x^2+y^2} дифференцируема по x и по y всюду, но вот смешанная производная существует не везде.

Этот мой затуп примечателен тем, что тянется как минимум с 1996 года. Я периодически о нём вспоминал, думал, что надо бы что-то с ним сделать, отвлекался и забывал снова.

9th February 2015

13:19: Про самодонесение
В общем-то, тему донесения на себя о наличии второго гражданства (или ПМЖ) обсосали уже со всех сторон. Я просто хочу привести одну цитату, которая, мне кажется, иллюстрирует мои опасения по этому поводу.

До сентября 1943 года Сен-Рафаэль был оккупирован итальянскими войсками. Они были человечны и иногда даже обуздывали усердие властей «Виши». Тем не менее начиная с лета 1942 года эти власти предписали всем французским подданным еврейского происхождения «для своей пользы» зарегистрироваться в полицейском участке по месту жительства. Туманные угрозы возмездия висели над нарушителями. Знакомые, уроженцы Франции еврейского происхождения, уговаривали меня последовать их примеру и зарегистрироваться во избежание неприятностей. Но мне казалось, что быть записанным «евреем» в полицейском участке — это уже начало неприятностей, и я воздержался. Впоследствии оказалось, что я был прав.
...
Осенью 1943 года я нашел, что достаточно примелькался в Сен-Рафаэле и что не плохо было бы переменить обстановку. Итак, в сентябре 1943 года я отправился в Гренобль,.. Я пообедал в вагоне-ресторане, где мой «визави», на которого я, очевидно, произвел хорошее впечатление, поделился со мной информацией, источником которой будто бы являлся его хороший знакомый, важный полицейский чиновник. «Всех этих евреев (он употребил другое слово), которые валяются на пляжах Ривьеры и чувствуют себя, как „у Христа за пазухой“, скоро заберут и рассадят по лагерям. Вполне возможно, что облава начнется сегодня ночью». «Я знаю одного, который от вас уйдет», — подумал я, расставаясь со своим «симпатичным» собеседником. Он был прав, и несколько несчастных, которые зарегистрировались в Сен-Рафаэле, исчезли в ту ночь.

©Анатоль Абрагам, «Время вспять, или физик, физик, где ты был» (автобиография)

Что там говорите? Проще сообщить о наличии второго гражданства и спать спокойно? Ну-ну.

14th January 2015

00:39: Szia!
Итак, я таки это сделал.

В настоящий момент я сижу в квартире в центре Будапешта. Что я там делаю? Пишу этот пост, естественно.

Шутка. На самом деле, я теперь работаю в компании Kinja, которая является венгерским отделением Gawker Media. Собственно, практически все разработчики Gawker-а сидят именно здесь.

С чего всё началось? Как ни странно, я могу назвать точную дату, когда я понял, что — пора. Пора экспортировать трактор куда-нибудь в более приличное место. То есть, не в Северную Корею. Белоруссию со скрипом, но рассматриваем (при Ельцине такого не было).

Ах да. Дата. 28 декабря 2012 года. День подписания ВВБХ закона подлецов.

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

Нельзя сказать, что до этого меня не посещала мысль уехать. В конце лета того же года — или в начале осени? не помню — я попробовал податься в Jane Street (Upd: оказывается, в Jane Street я пробовался уже после подписания "закона подлецов"). Не прошёл, но зато съездил бесплатно на собеседование в Лондон и лично познакомился с _adept_ — Дима, привет! Но тогда мне больше хотелось в Jane Street, чем в Англию (хотя туманный Альбион — кстати, не видел там тумана ни разу — меня тоже привлекал и привлекает). После того дня мне стало хотеться хоть куда-нибудь.

Тогда я стал методично бить в одну точку. Я знаю, ожидать разных результатов от одинаковых действий — симптом шизофрении, но в данном случае я предпочёл фразу генерала Сиверса: "лбом стену как раз-таки и прошибёшь, если только бить систематически". Точкой приложения усилий стал LinkedIn. Замечательное место, по крайней мере, для программиста. Конечно, я смотрел и в других местах — на FunctionalJobs, например — но это мелкие ручейки по сравнению с LinkedIn. Я подавался на всё, что хоть немного было мне по душе. Нет, на C++ вакансии я не подавался. И на PHP — тоже. А вот Erlang уже был подходящим, хотя и надоел до чёртиков на моей основной работе.

Кстати, об основной работе. После фиаско с Jane Street я устроился в Reksoft, в отдел, аутсорсящий для AlertLogic. Американцы выбрали Erlang как основной язык. Люди, истинно говорю вам: динамически типизированные языки — зло. Полнейшее. Этим пользоваться нельзя. Вернее, можно, для мелких скриптиков и тонкого клея между компонентами, написанными на чём-нибудь нормальном.

Но я отвлёкся.

В некоторых случаях мне отвечали кадровые агентства. Обжегшись несколько раз, я понял, что с этими дела иметь нельзя. И без того резюме будут читать скорее менеджеры, чем программисты; ещё одна прокладка, не понимающая вообще ничего, никому не нужна.

Порой меня приглашали на Skype-собеседования; часто дело доходило до тестового задания. Тестовое задание я делал всегда за одним исключением: как-то раз мне прислали в качестве задания текст "придумайте, что ещё нам прилепить к нашей системе, и сделайте; а мы заценим". Я тогда сдержался и послал их цензурно.

При этом я почти никогда не писал cover letters. Рассылал всё время одно и то же резюме, практически совпадающее с профилем на LinkedIn с добавкой некоторых мелких деталей под которые в профиле не нашлось граф. Я знаю, что все книжки рекомендуют делать не так; но я подозреваю, что их авторы гораздо больше зарабатывают публикацией этих книжек, чем реальной работой. Я же подавался на столько вакансий, что под каждую из них подгонять резюме — значит вконец свернуть себе всю креативность. Да ещё cover letter. Нет, я его писал иногда, когда в вакансии была какая-то особенно понравившаяся мне деталь, но очень короткие и строго по делу — "здравствуйте, мне у вас нравится то-то и то-то, я с этим знаком так-то и так-то".

И вот, в середине апреля 2014, мне ответили из компании Kinja. Надо сказать, что эти ребята сделали всё наоборот — сначала предложили тестовое задание, а потом уже назначили собеседование по Skype. Тестовое задание было в форме проекта на github, к которому требовалось прилепить некую функциональность. Мне пришлось разобраться в базе данных Redis, но, в общем, ничего сложного там не было. Я отправил результат, а заодно приложил ссылку на pull request, чтобы людям было удобнее смотреть, что я реально поменял.

После Skype-собеседования меня пригласили недельку поработать у них. Я взял на работе отпуск на неделю, забронировал билет на самолёт — и в конце июня прилетел к ним.

Будапешт мне сразу не понравился.

А вот на второй день, когда я выспался — понравился. Красивый город. Хотя, надо сказать, города я оцениваю не по красоте. По чему именно — не могу сказать. Но одни города мне нравятся (и их, надо сказать, большинство), а другие — нет. Лондон, Берлин, Париж, Кёльн, Буэнос-Айрес — понравились. Москва, Хельсинки и Мар-дель-Плата — нет. Сам не знаю, почему. Знаю только, что Будапешт уверенно вошёл в категорию нравящихся.

Будущие коллеги — тоже понравились. Вели они себя вполне приветливо, прилично говорили по-английски, и прекрасно понимали принцип "война войной, а обед по расписанию". Процесс разработки был организован ГОРАЗДО лучше, чем у AlertLogic; особенно меня покорило то, что выкатывание сделанного и протестированного кода осуществлялось, по сути, одной кнопкой, причём в любое время. В AlertLogic эта процедура проводится где-то раз в две недели и напоминает закат солнца вручную: один (а часто и не один) человек сидит на рабочем месте рано утром, давит кнопки, лично останавливает сервера, обновляет их, запускает, после чего пытается лихорадочно справиться с возникшими проблемами (каждый раз разными), и, если ему это не удаётся, возвращает всё взад, чтобы оно хоть как-то работало. До сих пор благодарю Летающего Макаронного Монстра, что отвёл от меня чашу сию.

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

Оформление бумажек заняло вечность.

Сначала был market test. Я не знаю, как это переводится на русский или венгерский. В общем, месяц вакансия висела на специальном сайте, куда нормальные люди не ходят, а мои работодатели заворачивали тех ненормальных, которые на неё подавались. Не знаю, сколько их было. Может быть, ни одного.

Затем, в августе, наконец были согласованы все документы, и я пошёл в консульство. Консульство Венгрии в Санкт-Петербурге находится в маленьком и уютном подвальчике. Со мной разговаривала сама мадам консул, напомнившая мне миссис Уэсли. Мы очень мило побеседовали, она приняла мои документы, сообщила, какие ещё требуются от меня и от приглашающей стороны, я на следующий день принёс свои и сообщил в Kinja, что нужно от них. Они весьма оперативно всё выслали (прямо в консульство по e-mail), и в начале сентября документы пошли в Венгрию. Там они застряли на три месяца. Только в декабре мне сообщили, что моё разрешение на работу согласовано, но, чтобы получить его, я должен приехать в Будапешт, можно по туристической визе. Я уволился из Reksoft, затем переждал Новый Год, и вот — я здесь. Кстати, разрешение я ещё не получил, оно ожидается в четверг — но, вроде, действительно уже готово.

Чуть отмотаю время назад. Где-то весной на меня вышел рекрутер из Google. Был он технически некомпетентен (например, пытался меня убедить, что quicksort бывает только один). Так или иначе, в Google меня тоже пригласили на очное собеседование. Вакансия предполагалась в Цюрихе, но собеседование проводилось в Петербурге. Не скажу, чтобы показал себя особенно хорошо (и предложения в итоге не получил). Но, с другой стороны, мне не слишком понравилось увиденное (хотя не надо думать, что я не поехал бы в Цюрих, возникни такая возможность). В частности, я задал там вопрос "много ли в вашей компании бюрократии". Меня, конечно, уверили, что очень немного. После этого я спросил "пишете ли вы в официальных бумагах свои цели на год". Мне сказали "а как же!" Тут я поперхнулся, так как незадолго до этого заполнял то же самое в Reksoft-е. Я сомневался, что меня поймут, если я напишу честно "хочу свалить из страны в целом и из вашей конторы в частности". В конце концов героическими усилиями мой начальник заставил меня что-то накропать под его диктовку — я забыл, что именно, уже через пятнадцать минут.

Кстати об этих чёртовых "целях на год". Это должны были быть не просто цели. Они должны были быть согласованы с целями компании. Среди которых была, например, такая замечательная вещь как "dominate the Cloud". Я не знаю, что это значит. По-моему, никто не знает. То есть, какие-то поясняющие слова на эту тему есть, но никто не удосужился перевести их с manager speak на нормальный.

Ещё одна деталь. Уже после того, как мне сказали, что меня берут в Kinja, но до того, как прислали официальное приглашение, я узнал — вместе со всеми Reksoft-овцами — что AlertLogic решил перевести весь наш отдел в Кардифф (Уэльс) и сделать нас своими официальными сотрудниками. Почему именно в Кардифф — потому что часть серверов AlertLogic-а находятся именно там. Я не был восхищён. Kinja уже тогда казалась мне интереснее. К тому же, когда нанимают тебя лично — это гораздо приятнее, чем когда нанимают всю команду, ну и тебя заодно. Тем не менее, я не сообщал, что ухожу в Kinja, до получения приглашения. Когда же получил его — объявил во всеуслышание, что, дескать, вы, ребята, можете сидеть на серверах, жрать доширак и доминировать клауд (это практически дословная цитата), а я поеду в другую сторону, писать на Scala.

В Кардифф я, кстати, съездил. Потратил там остаток отпуска и посмотрел, на что он похож. Ничего город, симпатичный. Но, видимо, уже (или пока?) не мой.

Не знаю, куда меня занесёт дальше, но, думаю, не в рашку (которую я планирую время от времени навещать, разумеется, пока у меня там есть родственники). Хотя, как говорится, от сумы да от тюрьмы...

16th November 2014

01:01: Interstellar
Until now, I always answered any questions about this movie with short remarks. Like "don't waste your time", or something like that. But now I have some free time, so I figured, why not try and review it properly? If anything, Nolan deserves to be taken seriously. When Coen brothers make something terrible, you just shrug and say "well, that's Coen brothers, it's what they do". But Nolan, let me remind you, created such masterpieces as "Inception" and "Memento", and "Batman begins" was pretty good too.

Just a warning. I'm going to put some spoilers here. Big ones. I'm not writing for the newspaper; I'm writing to my personal blog. So, you've been warned.

So, "Interstellar". Let's see what we've got.

1) Plot. A good plot seemed to be the greatest virtue of Nolan films so far. And it's very hard to make a good movie without a good plot. There are examples of that, like Bergman's "Smultronstället" ("Wild Strawberries"), but only a few of them. Even geniuses like Bergman do not succeed every time they attempt something like this.

And the plot here is really bad. Let me give you a few examples.

The characters are looking for a new home for Earth population. A while ago a few people were sent to different planets (only one astronaut per planet) and three of them sent back a confirmation that the planet is good and can serve it's purpose. Due to the lack of fuel, our heroes must choose which of those planets to visit, and they eventually choose one that was studied by the leader of the previous mission, Dr. Mann. He is still on the planet, and, after landing there, they'll take him back to Earth.

From that description it should be completely obvious to you (even if you didn't see the movie) that Dr. Mann is a liar who just (correctly) guessed that otherwise he won't be rescued. And that's exactly what happens here. The situation could be saved if Dr. Mann just admitted his lie and begged the main characters to forgive him. But no. Mann, unable to hide the fact that the planet can't be the new home of humanity, tries to kill everyone and get back to Earth alone on their spaceship. I don't know what could be more hackneyed than that. And, of course, he fails.

Another example. Why the Earth population even needs the new home? Because edible plants on Earth slowly die. It seems that there are two reasons for that: thick dust that somehow flows through the air and covers everything, and some pathogen. Yeah. So, instead of fixing the problem on Earth, they decide to just pick a completely different planet and try to make it habitable. Does it really seem easier? Well... not to me.

Or the use of aliens here. Aliens don't appear on screen (thankfully), but it seems that they created the wormhole in the Solar system, that allows humans to reach other planets. Throughout the movie, there is more evidence that aliens are, in fact, quite fond of humanity and did that specifically to facilitate humans' escape.

That much I can buy. There are additional questions — like, why didn't they create their wormhole closer to Earth, so that it would be easier to reach — but that's more or less OK. But later characters of the movie make two statements. First, aliens are "five-dimensional" creatures, they don't understand the concept of time, and, therefore, they need humans to do most of the job. And, secondly, aliens are really humans from the future, just very advanced.

You do see the problem here, right? Like, these two statements are in direct contradiction? What's more, neither of them is substantiated by the evidence, it's completely unclear how the main characters figured this out.

2) Acting. Not much to say here. Nolan, as usual, doesn't give the actors much to work with. While that worked with aforementioned "Inception" and was a bliss with "Memento", whose lead actor was just unable to act, here it doesn't work. Because of the difficulties with the plot. And Mr. McConaughey, who plays the main character here, isn't a good enough actor to overcome the limitations. In fact, as former space pilot turned farmer, who returns to being space pilot, he seems more interested, more alive, when he works on a farm, than when he flies the ship through space. And that contradicts his repeated statements that he hates the farm and was born to fly.

3) Scientific background. That was another thing that was well handled in, say, "Inception". Of course, the premise of "going into the mind of a sleeping person" was completely fantastical, but at least some consequences were well tied to the real world — like gravity. When the characters are sleeping in a van, and that van falls freely, there is no gravity in their dreams.

"Interstellar" instead sells itself as a "hard sci-fi", which follows the laws of physics as close as possible. OK, let me just give you an example.

"Hey, let's send a probe to the black hole, so that it would send back information about what's inside".

I kid you not.

And then they do exactly that. And a human — the main character — goes in along with the probe. What tidal forces?

At least this astronaut, as well as the robot doubling as a probe, didn't manage to send a signal outside. That's good. Why did he ever thought he would be able to do so is incomprehensible. I guess he was really stupid. But then there comes the part where he encounters a strange place inside a black hole that lets him communicate with his daughter who is still on Earth, sending all the collected data about black hole to her. In Morse code. Erm... isn't ASCII better for that? And, I don't know how much data he was sending, but I'm pretty certain that it was several megabytes at least. So... how long did his daughter stay there, writing down his messages? Months?

Anyway. If you want hard sci-fi, read "Martian" by Andy Weir, and do not watch this.

Was there anything good about the movie? Yes. But only one thing. Robots. Robots were helping people. Not one of them went on a murderous rampage due to some technical problems. And I especially liked the fact the were not made humanoid without any reason; instead, they generally were just big black boxes. Some thought was obviously given to their design, which made their movements (sometimes very fast) quite believable.

But that is not enough for a 3-hours movie. And, not to end this review on a positive note, here is, again, something regarding the plot.

I'm OK when an astronaut who landed in water says "Damn, our engines are wet, so, let's pump some oxigen into them; that way we would be able to make the fuel burn". I don't know if it's something that could work in reality, but at least it seems believable on screen. But, instead, we get this: "Damn, our engines are wet; we have to wait for an hour until they dry out, despite the fact that every second counts now. <skip almost an hour> Oh, there is a huge wave coming to us, and we need a few minutes more; let's pump some oxigen into the engines; that way we would be able to make the fuel burn". Erm... why didn't you do the same thing an hour ago? Or a minute ago, for that matter?

Bad movie. Bad Nolan. No cookie for him.

9th October 2014

18:05: When existentials aren't enough
> {-# LANGUAGE GADTs #-}
> {-# LANGUAGE RankNTypes #-}
> module Exists where
I've discovered a certain trick that seems to recur in my code. It might be a sign of me having too much time on my hands, which is commonly referred to as "perfectionism". Anyway, I thought I'd share it with the class. And forgive me if my exposition is overly generic; I do have real examples, but there is some extra stuff that obscures the general idea, which is what I want to present here.

Let's start with the simple example. Suppose we have some input, and want to generate some output from it (remember, I've told you it would be generic). Such "generator" is just a function of type
> type Gen1 m input output = input -> m output
Note that generation is done inside some arbitrary monad, so as to allow side effects.

How do we use such a function? Well, that's very simple, we just apply it and use it's result:
do output <- gen1 input
   process output
That's certainly good, but imagine that this generation has something existential about it. The output we generate has an additional argument — sometimes it would be a phony type intended to be a "marker" of this specific output (which is the case in my real-life code); sometimes it would read the type from external sources, etc.

Well, that's not a problem at all; existential types (superseded by GADTs) serve exactly that purpose. So, in this case we use an existential type
> data Exists output where Exists :: output phony -> Exists output
Then the generator would have a slightly different type
> type Gen2 m input output = input -> m (Exists output)
Using such a generator is almost as easy as the first one:
do e <- gen2 input
   case e of Exists output -> process output
In fact, we can easily convert the generator of the second type to the generator of the first type. The only thing we'd have to care about is adding this extra "phony" type to the output. We can do it like this:
> newtype WithPhony output phony = WithPhony output
Now, the actual conversion is done like this:
> withPhony :: Monad m => Gen2 m input (WithPhony output) -> Gen1 m input output
> withPhony gen2 = \input -> do {Exists (WithPhony output) <- gen2 input; return output}

Here comes the trick I've been talking about earlier. Assume that our generator is somewhat recursive, so that it's input depends on the output. What's worse, we need it to have this extra type as well. So the situation seems freaky: we have all the essential parts of input, then we need to generate output AND find out the "phony" type, so that we can amend our input. That's not usually a problem when there are no extra types; but with them in place things become interesting.

Let me give a (kinda real) example. Assume that you're creating a couple of tables in a database. You want to mark them with phony types, so that indices for one table are not accidentally used with another. Normally that would be like a situation described above, with existential types; but now you also want those two tables to reference one another. That mean that the specification (something like CREATE TABLE command) for each of them contains the phony type that marks another one. So we can't know the phony types beforehand; we can't define them externally, because then it would be possible to create two different table with the same marker type; and we need them as part of our input. Tricky.

So, returning to the general question, we have some function of type
> type ProduceInput m part input phony = part phony -> m (input phony)
Note that we don't use the full output; type safety aside, it's quite possible that we can't pass the full output "back in time", only the part of it.

Let's ask ourselves a question: suppose that we have the "generator" that produces our output, and it's type correct. When we use it — what would we have other than the generator itself?

Instead of input we would now have the function that produces the input from the part of output. Is that all?

No. We would also have the processing function, which takes the output and produces some result. It was present in the examples above, but it was not treated as part of the equation. But there is no reason why it shouldn't. And what's more exciting is that we don't really care about the extra types; we only care about the final outcome. So, we have this data:
> data Gen3Input m part input output phony result =
>     Gen3Input (part phony -> m (input phony)) (output phony -> m result)
And that's exactly what we need. Now our generator can be typed as this:
> type Gen3 m part input output =
>     forall result. (forall phony. Gen3Input m part input output phony result) -> m result
Using this generator is straightforward; in fact, it's even easier than using first two versions:
gen3 $ Gen3Input generateInput process
Writing this kind of generators is more challenging, but only because it does a more complicated job. Essentially the process is outlined above: we produce our input using the part of the output, not yet known; in the lazy world of Haskell this is completely normal, it's just that the input won't be generated fully until the next step. Then we compute the output from the input, using whatever techniques we need, and feed the required part of it back to the input producer.

For example, in case of two tables referencing each other, the only "part" we need is the markers themselves; so, this "part" would actually contain zero bits of real information. Therefore we can have our "input" (the specifications of tables) in full, and create tables from it.

We can, as before, convert this generator to the previous one. The part of the output we feed back would be void here.
> data Void phony = Void
We also add extra phony type parameter to input. The conversion itself is quite simple.
> withDep :: Monad m => Gen3 m Void (WithPhony input) output -> Gen2 m input output
> withDep gen3 = \input -> gen3 $ Gen3Input (\Void -> return (WithPhony input)) (return . Exists)
Note that here we don't use the full continuation as our processing function; we only need to advance it a bit farther than extra type goes. The existential type constructor gets rid of it, so we can stop there.

Well, class, that's it for today.

15th September 2014

15:13: Oldschooling
It's XXI century. We have flying cars now. What we also have, is a lot of research in programming languages. We have heavy books on refactoring. We have tons of "best practices". And then...

You know, a while ago I bitched about generics in Rust not being really generics, but rather templates, as they are specialized for every type they are used with. In fact, they are more limited than templates, as they don't allow partial specialization.

A few days ago I got wind that they finally woke up and implemented generics as, well, generics. I've checked immediately, and, no, we are still at the same spot.

But then another thought came: how can they specialize generic functions for types they know nothing about? I mean, if module A defines a generic function, and module B uses it with the type also defined in B, and A is compiled first — there is no possible way to specialize this function for the type defined in B, right? C++ avoids this by having all templates implemented in header files, so that they are available when compiling B — which really means that the function is NOT compiled with A (unless it uses it with some other type), but rather with B. Rust can't do that, after compiling the module you can safely remove the source code, and it will work fine.

And somebody answered my question with this: http://www.reddit.com/r/rust/comments/2c6pn0/how_does_rust_implement_calling_generic_functions/cjcio0p

Yep. They keep the source code in the compiled file.

Well, not EXACTLY the source code — the AST. But the difference is, in my book, négligeable.

Yeah. It's THAT bad.

On the other hand, there is Go — which is another new language, intended to fill the same niche as Rust — or at least a close one. It solves the problem with generics elegantly and efficiently — by not having generics. At all.

Which is fine, if there were hope that generics would emerge in some later version — like it was with Java. But it seems that Go people rather enjoy not having generics: "Lack of generics has bothered me a little, but copy and paste with a multiple-cursor editor really makes short work of it."

Yes, its XXI century, and the earth is doomed.

12th September 2014

15:52: Swifting
You know, I've tried Swift again, with the latest beta available, and it kinda works.

Still seems like something inside it is crashing every other second, but XCode holds and playground... playgrounds.

I even tried my generics test, and was able to make it work. So it seems that Swift has real generics (unlike Rust). Nice.

10th September 2014

22:55: Time wasting
Yesterday's Apple Keynote turned up to be a disappointment for me. There were four topics there — and all of them were not for me.

1) iPhone 6(+). I am a bit hesitant at this matter — I have yet to see the actual device, let alone feel it in my hand, but for now the seem too big. I don't mind big things — I carry an iPad with me almost everywhere — but this is the phone. I like my iPhone 4s, and even iPhone 5 seems a bit too big. And the one-handed mode? Looks like a dirty hack, which is not what I would expect from Apple.

2) Apple Watch. Well, I don't like watches. I don't like something on my wrist. Never did. At school, I was wearing a watch on a chain for some time. Now, I don't wear watches at all. I always have a phone with me, and it shows the time perfectly well.

Also, the Apple Watch home screen looks ugly.

3) Apply Pay. It's really great. But only if you have iPhone 6(+). So — not for me.

4) Music. Well, that was always the crappy part of Apple events. Nothing new here.

So. I might revert my judgement on iPhone 6 (and even Plus) if, upon closer examination, it would prove to be not so bulky as it looks. If that happens, Apple Pay might eventually become useful for me too (although it only works in US for now, and my chance of moving there is pretty slim). So, there is hope for me and Apple — but not much of it.

30th August 2014

20:16: Hating
There is a bunch of actors, who, by starring in the movie, ruin the movie completely.

By "starring" here I mean "playing somewhat significant part". Cameos usually don't hurt much — with a few exceptions.

By "ruin the movie" I mean "make the movie unbearable for me to watch". As my opinion is clearly superior to everyone else's, that is all that counts.

Here's the list, which I'll probably amend later:

Konstantin Khabenskiy
Johnny Depp
Bill Murray
Robin Williams
Adam Sandler
Jim Carrey
Steve Martin

Only Khabenskiy ruins everything he touches.

Amazingly, not a single female actor have made this list. I don't know what that means — either my tolerance to actresses is much bigger, or it's easier for the shitty male actor to become a big star than for a female one.

4th July 2014

14:09: Customizing
I hate customizability.

Don't get me wrong. I'm not saying having an "Options" window hidden somewhere is wrong. Quite the contrary. Only the smallest programs can work without it. Or the programs that are nothing but a big "Options" window (like, "System Preferences" in Mac OS X).

And the idea of enabling the user to change anything and everything, tweaking the program and setting every little detail so that it's just right, sounds very exciting. Kinda like communism — I mean, come on, who doesn't want to get whatever he needs, while giving back only as much as he can, right? And, like communism, it doesn't work. Or, if implemented (which, unfortunately, requires somewhat less efforts than establishing communism), it leads to macabre, to say the least.

The problem as I see it is that the resources are limited. They are always limited. And at some point the decision has to be made: either we figure out the best way to do something, and proceed accordingly, or we insert enough hooks so that the user can do it himself. It requires a lot of effort to do either of this things, but doing both at the same time is an order of magnitude more complicated. We don't just have to make things look and work good; we have to make sure that if the user changes something so that it no longer works as we imagined, the rest remains intact.

So, nobody goes both ways. Either we get the thing that just works — and if it doesn't work as we want, our only option is to discard it and get something else — or we get a piece of shit; a customizable piece of shit, but still.

I really prefer the first option. I'm really flexible; I don't mind going from Mac OS X at home to Windows 7 at work to Debian Linux in VM. What I don't want is to spend my time configuring tools so that they at least do their job. I don't want HammerFactory; I just want a really good hammer, and if the one I've bought doesn't suit me, I'll buy another.

What is really sad is that sometimes the first option just isn't available. There are some good IDEs out there (the best I've ever seen is probably Visual Studio — yes, seriously), but there is nothing that works for Haskell better than Emacs. And Emacs is a terrible IDE. Yes, you can customize it. No, you can't make it good. And when you find out you can't — well, they would laugh at you, call you names and say that it's all possible, you just haven't tried hard enough. Which is kinda true — but that's the point. I want to be an awesome Haskell programmer; I don't want to be an awesome Emacs developer. Why? Because I don't.

I'm not attacking open source, by the way. A couple of days ago I've had a "training" in the thing called "Rally". It's supposed to be a collaboration tool. Very enterprise-y. And yes — quite customizable. No, it doesn't work; but nobody cares. And yes, we are stuck with it. Management decision.

On the other hand, we have open source tools like "git", for example. Do you customize it a lot? I bet you don't. Yes, there are some customization parameters, like "user.name" (which git explicitly asks you to set), and there are ".gitignore" files — but those are minor things. Git just works, and works great.

So, customizability isn't a good thing. It's just an excuse for developers to make the user do their job, while ridiculing the user for failing.

19th June 2014

11:10: Switching
First, the link:

http://top.rbc.ru/society/18/06/2014/931125.shtml

If, by any chance, you don't read Russian, here is the essence of it: Russian parliament proposed taxation of using foreign words and expressions (what does that even mean? how on Earth the expression can be foreign without foreign words in it?) in public statements, if such statements are made using the official language of Russia (did they forget what language is official in Russia? or they intend to change it?).

As blogs are now considered public media, and I certainly don't want to violate that law, I think I'll switch to English from now on, so as to not make any public statements in Russian. I might consider switching back if they change the "official language" to something else.

7th June 2014

22:40: Стрижиное
Ну что, попробовал я таки этот Swift. Скачал бету XCode, распаковал, запустил. И доложу я вам: это штука с огроменным потенциалом. В смысле, ни фига не работает.

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

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

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

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

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

21st May 2014

13:55: Врачебное
Вот допустим, позвали меня помочь обуздать MS Word. Или какую-нибудь более специфичную (но распространённую) программку, неожиданно отказавшуюся работать.

Я ни про MS Word ни про эту программку почти ничего не знаю. Поэтому я начну читать википедию, гуглить, просматривать форумы и т.п. Но при этом я справлюсь с проблемой быстрее, чем совершеннейший непрофессионал. И моё решение практически наверняка будет хорошим - не повредит чужие документы, не снесёт винду нафиг, даже обои не поменяет. Более того, если я не справляюсь, я довольно быстро это пойму и посоветую, где лучше спросить.

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

Почему к врачам относятся иначе? Почему, если терапевт начинает читать ту же википедию и гуглить симптомы, его сразу записывают в олигофрены? Конечно, википедия может содержать чушь (и часто содержит). Но врач, по идее, профессионал, и в состоянии отфильтровать бредятину. Кроме того, если он таки профессионал, то википедия будет для него только стартовой площадкой. Получив из гугля ответ "дык это ж шонибуделёз", профессионал посмотрит в справочник и выяснит, что на шонибуделёз симптомы не похожи, и имелся в виду шонибуделит.

Понятно, что банальный бронхит врач должен уметь распознать и без гугля. Но что-нибудь редкое? Нехарактерные симптомы? Зачем ему хранить всё это в голове, когда есть замечательный внешний источник информации.

6th April 2014

13:33: Пракрым
Выскажусь-ка и я про наболевшую тему.

ИМХО, популярный мем "затокрымнаш" в корне неверен. Правда состоит в том, что Крым в кои-то веки ПЕРЕСТАЛ быть нашим.

Год назад я мог совершенно спокойно съездить в Крым в отпуск. Ну, паспорт был нужен, да, но это невеликая проблема. Виза не требовалась. По-украински я никогда говорить не умел, но проблем из-за этого не испытывал: в любом крымском магазине со мной с удовольствием общались по-русски, да и спросить дорогу я всегда мог на родном языке.

Иначе говоря: Крым был МОИМ.

Сейчас, когда эйфория по поводу высоких пенсий у крымчан проходит (и, вроде, очень быстро проходит), что останется? Кем я там буду? "Оккупанта-империалиста, рашен гоу хоум". Крым перестал быть моим. И не только моим.

Спасибо, как говорится, партии за это.

24th March 2014

16:30: Deque
Наткнулся тут на простенькую задачку: написать стек, поддерживающий, кроме стандартных push и pop, ещё и операцию min, которая возвращает минимальное значение из содержащихся в стеке. При этом все три операции должны выполняться за O(1).

Ну, задачка действительно простая, но меня натолкнула на мысль: а что, если вместо стека делать deque, т.е., массив элементов, к которому можно добавлять новые (и удалять старые) как с левого конца, так и с правого? При этом по-прежнему хочется делать min.

Пока что у меня получилась реализация, делающая pop_front, pop_back и min за O(1), а push_back и push_front - за амортизированное O(1).

Вопрос к коллегам: это где-нибудь есть?

22nd March 2014

15:21: За пятнадцать минут
Тут у ivan_gandhi обнаружилась запись1 примерно такого содержания:


Всем, кому неловко ощущать себя "диванной сотней", которая возмущается в интернете, но не может сделать ничего практического в борьбе с российскими оккупантами. Теперь такая возможность есть. Группа адекватной русской молодежи, не имеющей ничего общего с путиноидным быдлом, начинает кибервойну против путинского режима. Участвовать в ней может любой желающий - достаточно зайти на <ссылка удалена — migmit>, скачать и запустить маленькую программку. Понимаю рефлекторный ужас, вызываемый таким предложением, но НЕТ, это не вирус, программа никуда не устанавливается и не запускается автоматически при перезагрузке компьютера. Она включается ТОЛЬКО руками пользователя. Вы полностью контролируете, когда она работает, а когда - нет. Вы можете в любое время ее включать и выключать.
Программа НЕ меняет никакие файлы на Вашем компьютере, не загружает его память или процессор, и использует только свободную ширину канала Интернет. Другими словами, ее работа никак не повлияет на работу Вашего компьютера. Программа проверена IT-шниками из Правого сектора, которому нет никакого резона дискредитировать себя, агитируя на своем официальном сайте за установку программы, которая могла бы повредить его сторонникам.
Что она делает - ну, вы уже поняли ;) Если нет - читайте по ссылке выше. Атомные реакторы не взрывает и данные не крадет.
Опасно ли, что "за вами придут"? В мире не было прецедентов, чтобы приходили за пользователями, чей компьютер используется для DDoS-атак. Пользователь в таких случаях считается заведомо невиновной стороной, которая знать ничего не знает, да и слишком таких пользователей много.
Кибервойна продолжится до тех пор, пока российские оккупанты не покинут Крым и вообще территорию Украины.
Учитывая возможность блокировок, прошу максимально широко распространять данную инф-цию.


Що мы тут видим. Во-первых, сам факт "скачайте мою программку и запустите" — это уже тревожный звоночек. Необходима дальнейшая работа. Ладно, идём на сайт.

Скачиваем. Версия — только под винду, исходников нет. Ладно, тем лучше — случайно я её точно не запущу. Лежит на depositfiles (почему не на собственном сайте?). Лежит в виде ЗАПАРОЛЕННОГО АРХИВА, с именем "что-то-там_Pass_123.rar". Вы знаете, зачем выкладывать на depositfiles файл, защищённый паролем, включая сам пароль в имя файла? А я знаю. Чтобы антивирус на depositfiles не залез внутрь и не заорал благим матом.

Ладно, скачали, распаковали. Там один exeшник. Открываем его в emacs, и видим строчки "UPX", "UPX1"... Ладно, sudo port install upx +lzma, распаковываем и это. Открываем снова, и видим множество строчек типа "TImageList". Понятно, дельфи.

Дальше смотреть на файл глазами мне надоело. Взял я IDA Pro, загрузил. Смотрю для начала секцию импорта. Интересно, если на сайте написано


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


то зачем в этой программе WriteFile? Нет, конечно, может быть, линкер не выкинул лишнее, или пишется какой-нибудь простенький конфиг... но почему бы не посмотреть? Смотрим... и находим процедуру, создающую VB-скрипт, собирающий информацию об установленных антивирусах. Упс.

Дальше стало неинтересно. До кучи я загрузил программку на malwr.com. Антивирусы распакованный вариант не тронули, но сайт отрапортовал три вещи: во-первых, программка никакого DDoS-а даже не попыталась сделать, во-вторых, этот VB-скрипт действительно создала (то есть, это не дохлый код), и, в третьих, таки прописалась в автозагрузку.

Вот так вот.

Update:

1Перепост был случайным, по-видимому.

3rd February 2014

23:25: Не знаю Emacs и не хочу знать.
Несмотря на то, что пользую его постоянно.

Вот ПОЛНЫЙ список того, чем я реально пользуюсь в Emacs:

1) Базовые вещи, связанные с редактированием текста - открытие, просмотр, набор текста, перемещение курсора, выделение, копирование, вставка, удаление, отмена последнего действия (какой кретин решил, что "C-x u" это удобно???), сохранение. Notepad.exe может всё это и ещё чуть-чуть.

2) Чуть больше продвинутого редактирования: поиск и замена, в том числе по регэкспам, автодополнение (только по словам, имеющимся в открытых файлах).

3) Одновременная, в рамках одного окна, работа с несколькими файлами. В разных буферах, но в одном окне.

4) Абсолютно базовые вещи, связанные с поддержкой языка программирования. А именно: подсветка синтаксиса и более-менее разумная расстановка отступов. Сюда же: автозамена табуляций на пробелы.

5) Киллер-фича: возможность запускать REPL в том же окне (в другом фрейме). Сюда же: интеграция REPL с редактором, из которой я реально использую C-c C-l, C-c C-t и C-c C-i, причём последние два - довольно редко. Ещё сюда же: возможность использовать окно REPL по существу как то же самое окно редактора - за исключением ввода произвольного текста в произвольном месте (даже C-y работает). Ещё сюда же: грамотная подсветка в REPL.

6) Быстрый переход к месту ошибки. Помогает.

7) Даже как-то стыдно упоминать, но: полноэкранный режим работы, причём графический. У меня макось, и возможность убрать из поля зрения абсолютно всё очень приятна (ладно тулбар, я даже в меню не лазил уже несколько лет как).

ВСЁ!

А теперь то, что часто выставляется как фича, причём важная, и чем я не пользуюсь вообще.

1) Какие-либо системы управления проектами, системы автоматической сборки и т.п. То есть, я, конечно, пользуюсь ими (например, cabal-ом), но Emacs тут ни при чём, cabal запускается из терминала, либо, в крайнем случае, из того же REPL.

2) Системы контроля версий. Опять-таки, я их пользую (обычно hg), но из того же терминала. Мёржи делаются не в каком-то специальном режиме, а "как есть", в одном файле.

3) Средства удалённой работы. По-моему, редактировать нелокальный файл — некий идиотизм.

4) Сниппеты. У меня не такая хорошая память, и думать о том, на какой аккорд у меня повешен сниппет, я не имею ни малейшего желания.

5) Фолдинг. Вообще не понимаю, нафига он нужен. Если надо одновременно видеть две функции, проще разместить их рядом, а не сворачивать весь код между ними.

6) Теги, которые etags. Наличие :i в REPL позволяет в большинстве случаев получить всю необходимую информацию, вообще не заглядывая в исходник; если таки нужно посмотреть, что там, то в выводе :i сразу видно, в какой файл смотреть.

7) Языкозависимое автодополнение. Типа, я набрал точку после имени модуля, и сразу получил список функций в нём. Если сильно надо, я наберу :bro в REPL. И да, мне не лень ОДИН РАЗ набрать идентификатор типа insertWithKey, а в остальные разы я его получу примитивным автодополнением.

8) Доступ к документации. Для документации у меня открыт браузер, и я быстро найду всё, что нужно. А если я уже знаю функцию, которая мне интересна, то, как правило, кроме её типа мне не нужно ничего, а его я получу из REPL.

9) Работа с файловой системой. У меня есть командная строка, и я умею ею пользоваться.

10) Поиск по многим файлам. Это очень редко нужно, а когда нужно — я знаю grep-фу.

Список можно продолжать бесконечно.

Суммируя: единственное, что РЕАЛЬНО держит меня на Emacs'е — это REPL. Всё остальное, что мне нужно, есть в ЛЮБОЙ IDE и в почти любом редакторе для программистов. Но эта фича — ключевая. И да, Emacs — худший из имеющихся редакторов (кроме, возможно, Vim), если забыть про эту фичу. Но с ней получается, что остальные ещё хуже.

Более-менее приблизился к этому Sublime Text. Но искаропки оно работало, когда я на него смотрел, из рук вон плохо. Достаточно уже того, что подсветка в REPL была а) такая же, как в коде, и б) не отличала код, набранный мной, от результатов, выводимых выполняемой функцией. Вообще, у меня создалось впечатление, что там ещё кучу всего пилить и пилить.

Leksah и Yi у меня собрались только в текстовом режиме. Я люблю терминал, но редактор должен быть отдельно.

Всё. Выдохнул.

Update: OK, убедили, возможно когда-нибудь я стану пускать grep из Emacs.

Update 2: ещё одну полезную штуку вспомнил — htmlize.el, чтобы весь код, вместе с подсветкой, экспортировать в HTML.

23rd January 2014

19:11: Тут по поводу вебдева гонят
Обнаруженные на данный момент гонщики:
http://jakobz.livejournal.com/236681.html
http://gliv.livejournal.com/125078.html
http://tonsky.livejournal.com/285722.html
http://lionet.livejournal.com/130032.html

Я профессионально вебом не занимаюсь, но чисто для себя - очень даже. Предпочитаю Happstack, писал под Play (на Java!) и даже когда-то управлялся с уродским серверным диалектом жабоскрипта под названием KScript (когда работал в "Кодексе"). Так что вместо прогнозирования попробую заняться фантазированием, на тему "как бы было хорошо, если бы".

Мне хотелось бы видеть в вебе такое же разделение на "программу" и "документ", как в десктопах. Чтобы:

а) Программ относительно немного, по крайней мере, по сравнению с документами. На все страницы википедии будет полдюжины программ (там всё-таки есть разные страницы).

б) Документы не содержат исполняемый код. В данном случае, конечно, имеется в виду жабоскрипт. Сюда же, видимо, CSS.

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

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

А, да, прогноз: этого не будет.

15th January 2014

15:57: Мопед, в общем, не совсем мой...
...но нет ли желающих пописать на лучшем в мире объектно-ориентированном языке под названием Erlang? А то у нас в РекСофте стоит задача по-быстрому набрать ещё десяток человек на эрланг-проект (не передо мной стоит, слава монстру), а претенденты идут, мягко говоря, сТранНые.

Вилку зарплат не озвучу, ибо сам не знаю. Территориально - СПб, приблизительно равноудалённо от Площади Мужества, Пионерской, Чёрной Речки и, кажется, Удельной. В общем, добираться не очень. Но народ вменяемый. Erlang тоже.

7th January 2014

19:44: Булгаков — это голова
Дал объявление в spb_otdam_darom, предлагая свой старый, битый жизнью iPad. То, что началось в комментах и личных сообщениях, до боли напоминало классику:

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

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

iPad ушёл.

28th December 2013

23:29: По следам четвёртого раза
выкладываю видео и несколько фотографий.

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

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

Собственно, видео:



И немножко пикспама. Из начала номера:


Из середины:


Из конца:


Вместе с тренером:


И, до кучи: ещё один номер с бальными танцами (стандартом): http://www.youtube.com/watch?v=fiaYBk8QhyY

23rd December 2013

13:21: В четвёртый раз
потанцевали на отчётнике "Текилы". Опять в ДК Дзержинского.

Организовано всё было ГОРАЗДО лучше, чем в прошлый раз. Решили основную проблему - расписание; в этот раз его даже обогнали. Сделали, наконец-то, отдельные раздевалки для танцоров разного пола.

Мы были не единственные, кто танцевал бальные танцы. Сначала было выступление пенсионеров в стиле "два притопа, три прихлопа", потом мы, потом какая-то "Новогодняя самба" (я её не видел), и под конец ещё один преподаватель, Никита Вудрин, вышел вместе со своей группой (я не считал, но там было человек двенадцать как минимум) и станцевал латину. Точнее, там был короткий момент, когда сам Никита со своей партнёршей и ещё одна пара покрутили правый поворот в венском вальсе. Не уверен, но, кажется, под ту же музыку, под которую мы в своё время плясали фигурный вальс. Никита, кстати, поразил меня тем, что во время выступления не переставал жевать.

В общем, нам с нашим стандартом никто не мешал.

Собственно номер. Всю драматургию для него придумал я. Хореография - моя примерно на 75%. Ещё процентов двадцать придумала Оля, и пять - наш тренер, Лёша Задвигин. Налажать мы нигде особо не налажали, но мелкие помарки были - скажем, первый фолловэй в танго мы начали раньше времени. Я это вовремя заметил и попридержал движения, так что уже следующий такт пошёл в правильном ритме.

За основу номера я взял классическую латинскую тройку - самба (знакомство на карнавале), ча-ча-ча (продолжение знакомства в баре, где места поменьше) и румба (любовь). Сначала мне хотелось собственно это и станцевать, но меня хором убедили, что лучше танцевать стандарт. Я стал думать, как перенести это всё в стандарт, и в итоге пришёл к следующему: венский вальс (знакомство на балу; квикстеп тоже подошёл бы), танго (конфликт), и медленный вальс (любовь). Для конфликта нужен был повод, и я его нашёл: Оля нацепила на платье цветок, а я в конце венского вальса его сорвал и не стал отдавать. Как рифму к этому делу в конце номера я предложил ей целую розу (тут очень пригодилась "появляющаяся роза" из реквизита фокусников: её можно было сложить и спрятать в карман).

Фотографий пока нет, видео тоже. Обещают, что на этой неделе будут - я тогда сделаю новый пост.

13th December 2013

11:14: А мы монтажники-подводники
Решил перечитать повесть Джеффри Дженкинса "Берег скелетов". Когда-то давно читал её в подшивке какого-то журнала ("Вокруг света"? Не помню) и она мне очень нравилась. Это такой триллер про капитана рыболовного траулера.

Так вот. В какой-то момент случай в баре вызывает у него воспоминания о временах второй мировой, когда он командовал английской подлодкой и умудрился на короткое время стать равным самому морю. Среди прочего, он вспоминает, как один офицер выше его рангом задал ему вопрос - что самое нужное для подводной лодки? И он ответил тогда кратко и точно: "Свежий воздух и скорость".

И вот я думаю: ведь это ровно то, что нужно нам, программистам. Ну, правда, "свежий воздух" для нас - это "читабельность кода", а "скорость" нужна более асимптотическая, чем точная. Зато порядок, в котором они идут, тот, какой нужен.

Получается, что мы - своего рода подводники.

Вот.

20th October 2013

20:00: Яблоки и математика
Обнаружил тут, что можно не только набирать тексты в LaTeX-е на iPad-е, но и там же их компилировать. Попробовал — оказалось чрезвычайно удобно. Добавил к этому ещё и внешнюю клавиатуру (Logitech UltraThin Keyboard Cover), превращающую iPad в миниатюрный ноутбук — вообще зашибись.

Сразу оговариваюсь: нет, нетбук не лучше. iOS и тачскрин уделывают нетбуки как нечего делать.

Приложений, умеющих компилировать LaTeX, оказалось аж два: Tex Writer и TexPad. Чем отличаются:

1) Оба изначально устанавливают только базовые пакеты, остальное надо докачивать отдельно; однако, Tex Writer это делает автоматически (в момент компиляции документа, эти пакеты использующего), а TexPad — по команде пользователя. В частности, поддержку русского в babel Tex Writer включил сам, когда понадобилось.

2) Tex Writer позволяет передать документ (как .tex, так и .pdf, и вообще любой) в другое приложение (хотя жест, с помощью которого это делается, не вполне очевиден). TexPad — вещь в себе, вытащить из него готовый PDF можно только через iTunes.

3) Разный подход к синхронизации с DropBox: Tex Writer просто синхронизирует все файлы, о которых знает (кроме texmf-local, хотя можно и его тоже синхронизировать), а TexPad требует отдельных указаний: это синхронизировать, это нет. Распечатать через AirPrint могут оба.

4) В Tex Writer используется дурацкая система перемещения курсора — вместо стандартной лупы там какая-то фигня со скачущим текстом. Привыкнуть можно, но смысл не ясен. В TexPad — всё стандартно.

4.1) Опять же, в Tex Writer перемещение курсора стрелками (физической клавиатуры) в сочетании с модификаторами либо не работает, либо работает неправильно. Скажем, Option-Up по идее должна перемещать на абзац выше, а на самом деле перескакивает в начало файла. Option-Down, аналогично, должна перемещать на абзац ниже, а на самом деле ТОЖЕ перескакивает в начало файла. В TexPad всё стандартно.

5) В Tex Writer показываются номера строк, в TexPad — нет. В обоих это не настраивается.

6) Оба умеют показывать логи. Tex Writer опознаёт в них номера строк и делает их гиперссылками. TexPad полностью заменяет лог на табличку со списком ошибок и предупреждений (которые, опять же, являются гиперссылками), и только по второму нажатию позволяет посмотреть полный лог.

7) Есть вещи, которые работают в TexPad и не работают в Tex Writer — вроде бы, beamer и tikz в их числе. Я сам пока не пробовал.

По поводу 4.1 я отправил багрепорт, автор Tex Writer-а обещал поправить. Кроме того, по его же словам в текущей версии кириллица не поддерживается; на самом деле поддерживается, если делать
\usepackage[utf8]{inputenc}
\usepackage[english,russian]{babel}

Вариант с \usepackage{fontenc} не работает.

По пункту 4 я намекнул, что так делать не надо, но, похоже, автор упёрся.

Сам пока, вероятно, останусь на Tex Writer-е, пока не столкнусь с ограничениями.

Update: TikZ в Tex Writer работает (по крайней мере, простые примеры).

Update 2: То же относится к Beamer-у.
Powered by LiveJournal.com