вот такой драфт: http://migmit.info/personalfeed
MiguelRecent Entries | ||
|
|
You are viewing the most recent 50 entries.
17th May 201330th April 2013
: Роутинг
Что у нас есть для роутинга в WAI: Пакет wai-router требует wai < 1.3. Современный wai - 1.4.0. Пакет web-routes-wai требует wai < 1.4. Чуть-чуть не достаёт. Пакет wai-middleware-route требует http-types 0.7.*. Современный http-types - 0.8.0. Пакет wai-lite помечен с опечаткой как DEPCRECATED, рекомендует использовать simple. Пакет simple требует установленно postgresql. Никакого флага, чтобы это отключить, нет. Пакет wai-dispatch писал кто-то альтернативно одарённый, решивший, что к пути запроса вполне логично будет добавить метод, т.е. запрос к "http://server/a/b/c" роутится на ["GET", "a", "b", "c"]. Отразить это в документации этот кто-то не позаботился. Пакет wai-routes работает через Template Haskell. Что у нас есть для роутинга в Happstack: Базовый пакет happstack-server содержит всё необходимое. Что я упустил? 20th April 2013
: Покатушечное
Вообще-то, всё закончилось уже несколько часов как, но я только сейчас сподобился об этом написать. Итак, сегодня было торжественное открытие летнего велосипедного сезона в клубе "Велопитер". Отмечали это заездом на 60км (это они так говорят — по ощущениям, там было где-то 45, ну, максимум, 50). Я в клубе не состою, но скатался тоже. Почти всю дорогу моросил дождь, распогодилось только к концу. Некоторые ехали в плащах-дождевиках. Я ехал так, и, в общем, промок не сильно. Отдельные у-о ехали на велосипедах без крыльев — за одним таким я некоторое время следовал; представьте — от колеса летит фонтан жидкой грязи, причём (так как крыла нет) летит не назад, а почти вертикально вверх и даже чуть-чуть вперёд, и упирается непосредственно в свисающую с седла задницу, точно посередине. У меня велосипед простецкий, но с полноценным крылом, до середины колеса. Переключения скоростей нет — и я не оценил, чего лишился. Зато есть ножной тормоз (редкость в наши дни), без которого, по-моему, ездить сложно. Ехал я ближе к хвосту колонны — я не слишком высоко оцениваю свои способности как велосипедиста. Обошлось без происшествий, если не считать того, что у меня посреди дороги разболталось зеркало, и я остановился его подтянуть. Очень порадовался при этом, что перед выходом сунул в рюкзак разводной ключ — он у меня хороший, 12-дюймовый, затягивает гайку любого размера, откручивает даже то, что приварено, и если нападут разбойники — им очень сподручно отмахиваться. Перед самым началом заезда я затоварился в ближайшем магазине перчатками — замёрзли руки. Вполне правильно затоварился, если бы не они, я бы закоченел. В целом, покатались вполне приятно. У меня был с собой айфон, но фотографировал я мало, не до того было. Одну фотографию, всё-таки, выкладываю — я её подрезал слегка, чтобы понятнее было. ( Read more...Collapse ) 11th April 2013
: Путезависимое
Что-то работается сегодня не очень. Напишу, пожалуй, что-нибудь сине-зелёное.
> {-# LANGUAGE Rank2Types #-}
> module PathDependent whereВот, есть такой классический тип Set. Множество, стало быть. Как известно, оно требует, чтобы элементы, лежащие в нём, принадлежали классу Ord. Ну, внутри у него сбалансированное дерево, которое без упорядоченности не может.Так вот, как известно, ограничения вида Ord a => ... — это просто ещё один аргумент в функцию. Только неявный. И этот аргумент может, при некоторых условиях, оказаться в разных случаях разным (хотя это и противоречит идеологии классов). Ну, вот может.То есть, фактически, интерфейс, предоставляемый типом Set примерно такой (я привожу интерфейс не полностью, а на "реализацию" рекомендую не смотреть вообще, она здесь только для примера):> data Set a = Set > empty :: Set a > empty = Set > insert :: (a -> a -> Ordering) -> a -> Set a -> Set a > insert _ _ ~Set = Set > check :: (a -> a -> Ordering) -> a -> Set a -> Bool > check _ _ ~Set = False Ну и, естественно, здесь мы уже вообще не контролируем, что первый аргумент (типа (a -> a -> Ordering)) будет одним и тем же. А если он будет меняться — наше множество непременно поплывёт. Может, например, оказаться, что один и тот же элемент окажется в нём несколько раз. Или, что ещё хуже, что функция check не найдёт в множестве элемент, который там точно есть. И так далее.Соответственно, нам нужно не передавать каждый раз функцию сравнения в аргументе, а хранить её где-то ещё. И первое, что приходит в голову - хранить её в самом множестве. Вот так, например: > data GSet a = GSet (a -> a -> Ordering) (Set a) Так, действительно, можно обезопасить указанный интерфейс. Функцию сравнения придётся перенести в другое место, но это не страшно: > emptyG :: (a -> a -> Ordering) -> GSet a > emptyG comparator = GSet comparator empty > insertG :: a -> GSet a -> GSet a > insertG a (GSet comparator s) = GSet comparator (insert comparator a s) > checkG :: a -> GSet a -> Bool > checkG a (GSet comparator s) = check comparator a s Всё, казалось бы, хорошо. Проблемы начинаются, когда множеств-аргументов оказывается больше одного. Например, операция объединения рушит всю схему: > union :: (a -> a -> Ordering) -> Set a -> Set a -> Set a > union _ ~Set ~Set = Set unionG :: GSet a -> GSet a -> GSet a unionG (GSet comparator1 s1) (GSet comparator2 s2) = GSet ??? (union ??? s1 s2) И что ставить вместо ???? Один из компараторов? Который? А второй что — потерять? А то, что операция получится некоммутативной — ничего? Или поставить один в одном месте, другой — в другом?Единственный разумный выход — это преобразовать одно из множеств в список и добавить его элементы во второе по одному. Но это — медленная операция. По крайней мере, медленная в сравнении с объединением упорядоченных деревьев. На этом месте продвинутые читатели подумали "Ха! Зависимые типы!" И да, если бы в Хаскеле были зависимые типы, то мы написали бы что-то в таком духе: newtype SetDT a (comparator :: a -> a -> Ordering) = SetDT (Set a) Теперь компаратор хранится не в самом множестве, а в его типе. Поэтому за совпадением этих компараторов может проследить автоматика, то есть, система типов. Интерфейс получается примерно таким: emptyDT :: (comparator :: a -> a -> Ordering) -> SetDT a comparator emptyDT _ = SetDT empty insertDT :: a -> SetDT a comparator -> SetDT a comparator insertDT a ~(SetDT s) = SetDT (insert comparator a s) checkDT :: a -> SetDT a comparator -> Bool checkDT a ~(SetDT s) = check comparator a s unionDT :: SetDT a comparator -> SetDT a comparator -> SetDT a comparator unionDT (SetDT s1) (SetDT s2) = SetDT (union comparator s1 s2) Собственно говоря, полноценные зависимые типы не обязательны, достаточно того, что в Scala называется path-dependent types. Это более слабая, но и более простая фишка. Суть в том, что тип зависит не от значения, а от указателя на значение. В результате требуемые зависимыми типами доказательства исчезают — зачем доказывать, что два значения совпадают, когда они хранятся в одной переменной. Так что, Scala может сделать безопасный интерфейс, а мы не можем? Не бывать такому! Добавим новый тип, который будет своего рода "флагом" для нашего значения: > newtype Comparator r a = Comparator (a -> a -> Ordering) > newtype SetPD r a = SetPD (Set a) Тип r здесь как бы ни при чём, он нигде не используется. Но он поможет нам удостовериться, что компараторы одинаковые.Интерфейс получается практически идентичный тому, который был приведён в начале поста: > emptyPD :: SetPD r a > emptyPD = SetPD empty > insertPD :: Comparator r a -> a -> SetPD r a -> SetPD r a > insertPD (Comparator cmp) a (SetPD s) = SetPD (insert cmp a s) > checkPD :: Comparator r a -> a -> SetPD r a -> Bool > checkPD (Comparator cmp) a (SetPD s) = check cmp a s > unionPD :: Comparator r a -> SetPD r a -> SetPD r a -> SetPD r a > unionPD (Comparator cmp) (SetPD s1) (SetPD s2) = SetPD (union cmp s1 s2) Ну что ж, это всё хорошо, но мы не решили проблему, а сдвинули её. Теперь нам нужно как-то гарантировать, что все используемые нами значения типа Comparator r a совпадают. Точнее даже, что мы используем только одно значение этого типа. И в этом нам поможет полиморфизм, вместе с такой функцией:> withComparator :: (a -> a -> Ordering) -> (forall r. Comparator r a -> b) -> b > withComparator cmp h = h $ Comparator cmp Фокус в том, что мы не будем экспортировать конструкторы для типов Comparator и SetPD. Мы экспортируем только интерфейс SetPD и функцию withComparator. А значит, эта функция окажется единственным способом создать компаратор. При этом она не даст нам создать компараторы одного типа — точнее, полиморфизм не позволит нам утверждать, что компараторы относятся к одному типу, а это ничем не хуже. Ну, разумеется, если кто-то решит поработать функцией unsafeCoerce, то он сам себе злобный буратино.Проверяем, как оно работает. Первый тест: > test1 = > withComparator compare $ \c -> > checkPD c 1 $ insertPD c 2 $ insertPD c 1 $ emptyPD Этот тест проходит совершенно нормально, он вполне законный. Попробуем использовать два разных компаратора: > test2 = > withComparator compare $ \c1 -> > withComparator (flip compare) $ \c2 -> > checkPD c1 1 $ insertPD c2 2 $ emptyPD Получим ошибку компиляции:
C:\Users\m.mitrofanov\Dropbox\PathDependent.lhs:58:22:
Couldn't match type `r1' with `r'
`r1' is a rigid type variable bound by
a type expected by the context: Comparator r1 Integer -> Bool
at PathDependent.lhs:57:7
`r' is a rigid type variable bound by
a type expected by the context: Comparator r Integer -> Bool
at PathDependent.lhs:56:7
Expected type: SetPD r Integer
Actual type: SetPD r1 Integer
In the second argument of `($)', namely `insertPD c2 2 $ emptyPD'
In the expression: checkPD c1 1 $ insertPD c2 2 $ emptyPD
In the second argument of `($)', namely
`\ c2 -> checkPD c1 1 $ insertPD c2 2 $ emptyPD'
Failed, modules loaded: none.Полиморфизм вообще не позволит нам вытащить ни множества, ни сам компаратор, из аргумента функции withComparator:> test3 = > withComparator compare $ \c -> > insertPD c 2 $ insertPD c 1 $ emptyPD Получаем, опять-таки, ошибку:
C:\Users\m.mitrofanov\Dropbox\PathDependent.lhs:52:7:
Couldn't match expected type `b' with actual type `SetPD r Integer'
`b' is a rigid type variable bound by
the inferred type of test3 :: b
at PathDependent.lhs:50:3
In the expression: insertPD c 2 $ insertPD c 1 $ emptyPD
In the second argument of `($)', namely
`\ c -> insertPD c 2 $ insertPD c 1 $ emptyPD'
In the expression:
withComparator compare
$ \ c -> insertPD c 2 $ insertPD c 1 $ emptyPD
Failed, modules loaded: none.Интерфейс полностью безопасен. При этом нам никто не мешает аргумент функции withComparator разбивать на подфункции, которые знать не знают ничего ни про какие {-# LANGUAGE Rank2Types #-}. Главное, чтобы они сохраняли полиморфность по r.Вот. 23rd March 2013
: То, что надо
Кинули тут в твиттере линк: http://hackage.haskell.org/trac/ghc/tic Вкратце: тайпклассы БЕЗ параметров. У такого тайпкласса, естественно, может быть только один инстанс, так что кажется, что это то же самое, что и просто значение. Но один юзкейс я вижу. Это форвард-декларации. То есть "я предоставлю это значение, но не здесь, а в другом модуле, который сюда импортировать не хочу или не могу". То, что в каких-нибудь плюсах есть просто с рождения. Для типов, кстати, форвард-декларации есть, это семейства типов без параметров. Они работают прекрасно. Пейтон-Джонса, в итоге, убедили, что реализовать это надо. 14th March 2013
: Тонущий пост
Ну, главную новость дня все слышали, да? Нет, я не про папу римского. Кому на фиг нужен этот папа римский? Если его кому-то не хватает, пусть кликнет по десктопу правой кнопкой мыши и выберет пункт "создать папку". Я про гугль-ридер. Чем, собственно, он был (и пока остаётся) так хорош для меня? 1) Облачный сервис. Да, я часто пользуюсь ридером с разных компов. С десктопных (коих тоже не один) я, как правило, только добавляю RSS. С мобильных (и их не один) - читаю. И хочу видеть только то, что ещё не прочитал, ни с этого устройства, ни с другого. 2) iOS-приложение для доступа к нему. Более того: не одно приложение. Дофигищи приложений, если уж на то пошло. Я пользовался NewsRack. Нет, я не помню, почему я не стал пользоваться <вставьте своё любимое приложение>. Это было давно. Всё, что мне нужно - это читать, и добавлять в Instapaper. Собственно, всё. Мне глубоко по барабану sharing. Читать чужой пост только потому, что его читает твой "френд" - это полный идиотизм. Особенно если этот френд не удосужился черкануть пару строчек о том, чем этот пост так уж интересен. Мне глубоко плевать на веб-интерфейс ридера. Не буду врать, я им пользовался, но раза три в год. Я не пострадал бы, если бы его не было. Мне глубоко пофиг "звёзды" - если мне понравится чей-то пост, я его букмаркну. Но пункты 1 и 2 были killer features. Я не знаю, как быть без них. 23rd February 2013
: Сообщения об ошибках
Среди компьютерно грамотного населения ходит в разных вариантах байка о тупом юзере, который увидел на экране сообщение об ошибке, не прочитал его, нажал "OK", а потом побежал спрашивать "А что это было?" Я не знаю, как у вас, Чаще всего это юридические тексты. Что очень печально, так как такой текст может сильно ушибить. Но бывают и другие, вот, у При выборе варианта в парах внутри них – внутри их (с н перед местоимением 3-го лица или без н) следует исходить из того, что в современном языке указанный звук добавляется, если местоимение стоит после любого из простых, или первообразных, предлогов (без, в, для, до, за, из, к, на, над, о, от, по, под, перед, при, про, с, у, через), а также после многих наречных предлогов (возле, вокруг, впереди, мимо, напротив, около, после, посреди, сзади и некоторых других, употребляющихся с родительным падежом). Однако такие предлоги, как внутри, вне, употребляются в основном без начального н.Ну невозможно же! Так вот. Это, товарищи, не бухгалтер плохой, потому что сообщения об ошибках не читает. Это программу идиоты писали, сделав такое сообщение об ошибке, которое бухгалтер физически не в состоянии осилить. 8th February 2013
: Instapaper
Вопрос. Сайт http://www.instapaper.com предназначен для того, чтобы закинуть туда ссылку, а потом, возможно, за другим компьютером, по этой ссылке пройти и прочитать. Почему сразу не прочитать? Времени нет, канал паршивый, просто в лом... масса причин. Поскольку пользоваться им очень удобно (есть расширение для файера, которое позволяет одним кликом отправлять туда текущую страницу, есть модуль для iCab Mobile, Tweetbot умеет отправлять туда ссылку из текущего твита, и т.п.), то он со временем становится хранилищем любопытных ссылочек, которые, например, можно упомянуть в разговоре. В принципе, для этого предназначен Delicious, но им пользоваться не так удобно. Но есть один недостаток. Имя ему - картинки. Искать картинку, которую когда-то видел, по имени файла - дохлый номер. Писать каждый раз пространный комментарий - тоже дохлый номер. А предпросмотра в Instapaper нет. Так вот (добрались до сути). Нет ли аналогичного сервиса - но для картинок? Что нужно: 1) Хранение ссылок на картинки. (ваш к.о.) 2) Предпросмотр картинок - может быть, с масштабированием, или ещё как-то. Необязательно, чтобы эти превьюшки хранились именно на этом сервисе, можно тягать их с исходных адресов и масштабировать css-ом. В сущности, даже желательно, чтобы так происходило - а то возможно, что картинка исчезнет, а превьюшка останется. 3) Экстеншен к файерфоксу, позволяющий добавить картинку через её контекстное меню. 4) Букмарклет - чтобы делать то же самое из любого браузера. 5) Хорошо бы: какой-то способ разграничить показываемые картинки. Чтобы, например, открыв на работе свой список сохранённого добра ты не выставлял на всеобщее обозрение картинки с любимых порносайтов. Кто-нибудь что-нибудь подобное знает? 3rd February 2013
: Интересно, типа.
Вот есть, типа, такая типа штука, называется типа "зависимые типы". Когда сам тип содержит в себе значение. И два таких типа будут (типа) совместимы, если эти значения равны. А поскольку значения будут известны только в рантайме, приходится давать доказательство того, что значения таки равны. А вот во всяких джавах с сиплюсплюсами равенств как минимум два. Не помню точно, как они называются, но, в общем, равенство по ссылке и по значению. Первое сильнее второго. То есть, если какие-то штуки лежат в одном и том же месте, то это одна и та же штука, и, значит, они одинаковые. Но если они одинаковые — это ещё не значит, что они лежат в одном и том же месте. В агде, если мой эклер меня не обманывает, требуется равенство по значению. А есть ли зависимые типы с равенством по ссылке? То есть, для того, чтобы Vector n Int был тем же типом, что и Vector m Int нужно не только n=m, нужно, чтобы n и m были одной и той же переменной.Такое где-нибудь есть? Update: подсказали, что это, вроде, называется "path dependent types". 1st February 2013
: Команда ублюдков
решила, что они лучше всех знают, какой интернет народу нужен. Пока, как я понимаю, в Костромской области.
Список уродов из попечительского совета этой "лиги":
Пока они милостиво дают право отказаться от этого. Пока. Update: администрация Костромской области пошла на попятный:
Маразм остаётся маразмом, но, кажется, не столь фатальным. 11th January 2013
: ВЛИУСЕРное, для
Контекст: интервью Рукшина. Ещё немного контекста, если кто не знает. С.Е. Рукшин - человек, лично ответственный за разрушение образования в 239-й школе. Оседлав волну превращения хобби для яйцеголовых "математические олимпиады" в большой спорт, он создал структуру кружков, которая погребла под собой всё остальное. Настолько, что человек, добившийся успехов в олимпиадном спорте, может не беспокоиться вообще ни о чём. Говорю по собственному опыту - я лично объехал выпускные экзамены именно с помощью олимпиад. Правда, кружковщины я в своё время избежал, и до сих пор этому радуюсь. Поэтому заявления типа "он умудрился взрастить двух филдсовских лауреатов" иначе как БСК ("бред сивой кобылы", а не цвета российского флага) я назвать не могу. В конце концов, некоторое время Рукшин рассказывал в школе, что я, якобы, его ученик. Для буквоедов: да, когда перед Рукшиным замаячила морковка звания "Соросовский учитель", он полгода вёл у нас какой-то бред (потому что "Соросовского учителя" давали только тем, кто реально преподаёт в школе, а не только кружки ведёт). Далее, собственно по "интервью". Интервью строится по простенькой демагогической схеме: "они со мной не согласны, значит, они козлы и уроды ваще". Аргументация, естественно, на нуле. Например: "Более 60% населения не одобряют ЕГЭ, но он входит в закон". Это - единственный аргумент против ЕГЭ, который приводит Рукшин. Думаю, фразу "миллионы мух не могут ошибаться" он знает, но ему плевать. Справка: я сам в своё время был горячим противником ЕГЭ. Первые экзамены, особенно по математике, были просто ужасны. Прошло немного времени. Я видел ЕГЭ, который сдавал в прошлом году мой брат. То, что было "ужас-ужас-ужас", стало более чем достойным экзаменом. И теперь я лично считаю, что ЕГЭ - лучшее, что было за последние лет двадцать сделано в образовании. Кстати, мне интересно, почему Рукшин выступает против системы, исключающей взятки? Бред по поводу "Гражданами России нас делает блаблабла" я даже комментировать не хочу. С таким подходом надо возрождать пионерлагеря и игру "Зарница". Встречаются в интервью удивительные вещи. Например: "в списки победителей финалов всероссийских олимпиад много лет вносились фамилии детей, которые в них вообще не участвовали. Соответственно, они получили денежные премии и льготы при поступлении в вуз" - извините, но я НИКОГДА не поверю, что Рукшин этих списков не видел. В целом, Рукшин стоит за сохранение старой системы. Системы, которая начала гнить задолго до моего рождения, и, к настоящему моменту, представляет собой труп. Кое-где ещё есть гальванические подёргивания, но более ничего. Рукшин в этой системе уютно устроился, он может в ней изображать, будто бы он делает что-то полезное и вообще хорошее. Мне лично представляется, что если реформы не сделают вообще ничего, кроме скидывания Рукшина с его холмика, они уже будут оправданы. И, в любом случае, состояние нашего образования настолько катастрофично, что хуже уже гарантированно не будет. 25th December 2012
: Юрьев день
Наткнулся на язык Rust. Позиционирует себя как "C, сделанный человеком, знающим Haskell". Читаю официальный туториал — и понимаю: да, так и есть. Алгебраические типы. Замыкания. Классы типов (называемые трейтами, но всем ведь всё понятно). И одновременно — язык достаточно близок к машине: не чисто функциональный, с указателями (причём, безопасными) — ну супер. А потом натыкаюсь на такой пассаж: The Rust compiler compiles generic functions very efficiently by monomorphizing them. Monomorphization is a fancy name for a simple idea: generate a separate copy of each generic function at each call site, a copy that is specialized to the argument types and can thus be optimized specifically for them. In this respect, Rust's generics have similar performance characteristics to C++ templates.Ып. Вот тебе, бабушка, и сабж. Никакие это не дженерики, а обычные темплейты, только обрезанные. Попробовал на всякий случай свой тест — и, ЧСХ, Rust его зафейлил с совершенно невнятным сообщением об ошибке. И сразу у меня впечатление, что делал этот язык не "человек, знающий Haskell", а "человек, прочитавший пару страничек из Душкина". 18th December 2012
: Для памяти
К настоящему моменту почти пятьдесят тысяч детей из России нашли приёмных родителей в США. Шестнадцать из них были убиты приёмными родителями. Дэвид Полрейс, убит матерью Рене. 18 лет тюрьмы (выпущена досрочно через 10 лет за хорошее поведение). Логан Хиггинботэм, убита матерью Лаурой (вина не была доказана; подсудимая признала себя виновной, так как в обмен ей разрешили сохранить другую дочь). Один год. Виктор Маттей, убит родителями. Оба получили 10 лет. Люк Эванс, убит матерью. Мать оправдана. Джейкоб Линдорф, убит матерью. Шесть лет. Захария Хигир, убит матерью (признала свою вину). Четыре года. Мария Беннет, убита матерью Сьюзен (признала свою вину). Три года. Джессика Хэгман, убита матерью Патрицией (признала свою вину). Десять лет условно. Лиам Томпсон, убит обоими родителями (отец обварил его кипятком, мать очень долго не обращалась к врачу). Оба родителя получили по 15 лет. Алекс Павлис, убит матерью Ирмой. Мать получила 12 лет. Денис Мерримэн, заморен родителями до смерти. Родители сели на 19 лет. Нина Хилт, убита матерью Пегги. 25 лет. Исаак Дистра, убит отцом Брайаном. Отец оправдан. Николай Емельянцев (фамилия — по приёмному отцу, гражданину России), убит матерью Кимберли. 15 лет. Чейз Харрисон, забыт отцом Майлзом в машине, где умер от перегрева. Отец оправдан. Натаниэль Кравер, убит обоими родителями. Родители отбыли только досрочное (полтора года), кроме того, лишились дочери Элизабет, также приёмной. Итого, из 16 случаев только три оправдания, плюс одно условное, плюс ещё один случай, когда суд ограничился досрочным. Мне интересно, сможет ли кто-нибудь перечислить поимённо детей, убитых русскими приёмными родителями. И сроки. 3rd December 2012
: Аччёт.
Коротко: фух. Длинно: Позавчера, в субботу, снова был отчётник Tequila Dance. На этот раз не под открытым небом, а в ДК Дзержинского (который уже давно не Дзержинского, но всё равно так называется). Мы танцевали дважды, медленный вальс в первом отделении (концерта, а не того, что вы подумали), и фигурный вальс (на основе венского) во втором. Всё прошло гораздо спокойнее, чем в первый раз. Опыт какой-никакой уже имелся. К тому же, нас незадолго до выступления напугали, что, дескать, пол на этой сцене такой, что танцевать вообще невозможно - а оказалось, что он вполне нормальный, видали мы паркет похуже. Зрителей я опять не видел, но не из-за волнения, а из-за того, что сцена освещена, а зал - нет; ну и потому, что не всматривался сильно, некогда было. Были ли мы лучше других? Вероятно, но точно сказать не могу: мы практически не видели другие номера. В отличие, опять-таки, от первого раза, мы большую часть времени перед выступлениями потратили на последнюю репетицию. Я первый раз в жизни танцевал во фраке. Честно говоря, разницы особой не почувствовал, разве что воротничок упирался в подбородок; но это не мешало. Фотографий почти нет, а те, что есть, плохого качества. Видео есть, тоже плохого качества, но уж чем богаты. Организаторы обещают через пару дней подогнать своё видео - не знаю, может, будет лучше. На видео с фигурным вальсом не виден наш выход на поклон, когда мы прогнали со сцены ведущую - она решила, раз музыка кончилась, значит, можно объявлять следующий номер; и тут мы снова появляемся. Может, на официальном видео будет. Энджой: 8th November 2012
: Просветительский пост по заказу
Утверждение, что на понимание чего угодно требуется 10 лет, не имеет ничего общего с действительностью. 31st October 2012
: Доделал библиотеку
Лежит на Hackage. Называется по-простому - plat. Это, собственно, те темплейты, которые мне были нужны. Собственно, выложил я это дело ещё вчера, но собралось оно на сервере Hackage только сегодня. Библиотека маленькая, поэтому экспортирует ровно один модуль, хотя исходников заметно больше. В самих исходниках чистый Haskell98, вполне себе Safe, но при этом активно используется mtl (которая не Haskell98). Причём это не было самоцелью - я был вполне готов использовать расширения, если понадобится. Не понадобилось. Даже один инстанс, который, как я думал, будет лучше написать через GeneralizedNewtypeDeriving, на поверку оказался немного не таким. Из возможностей - вставка строк (естественно), итерация, ветвление, проверка булевских условий. Немного, но то, что нужно. 18th October 2012
:
Хотел написать программный пост про то, как я не люблю генераторы кода. Причём любые. Сюда попадают макросы: CPP, Лисп (считай, целиком), Template Haskell, да всё подряд. И внешние препроцессоры — например, тот же CPP, когда он используется отдельно от C. Отдельной строкой сюда идут шаблонизаторы, генерирующие программный код — как в HSP, скажем. И всякие безумные тулзы, генерирующие "заготовки" сайтов, как это делает, например, Snap (и я уж молчу про разные джанги).
Не получилось. Потому что есть один язык генерации кода, который я могу терпеть. Он абсолютно ублюдочен. По сравнению с ним, синтаксис того же Template Haskell — это произведение искусства. Он завязан на единственный язык, и оторвать его невозможно. Его не получится использовать без интенсивной гуглёжки. И всё же я могу терпеть его наличие в коде и даже писать на нём сам. Называется — шаблоны C++. 14th October 2012
: Библиотеки
Слушайте, это я плохо ищу, или никто так и не сподобился написать нормальную библиотеку темплейтов для хаскеля? Что пробовал: HStringTemplate не ставится (ему нужно syb-with-class — только не спрашивайте, зачем — а оно не компилируется). Heist требует вывернуться ужом, чтобы сделать простейший цикл. Twine не даёт использовать в шаблоне собственный тип данных без UndecidableInstances (НАФИГА???) Hastache каждый раз заново переинтерпретирует шаблон. Куча библиотек, требующих использовать template haskell. Ещё несколько библиотек, обрабатывающих шаблоны внешней утилитой (то есть, тот же template haskell, только ещё хуже). У меня страшное подозрение, что библиотеки темплейтов пишут исключительно идиоты. В итоге я использую банальный Text.Html и чувствую себя полным бараном. 5th October 2012
: GeneralizedNewtypeDeriving
> {-# LANGUAGE GADTs #-}и > {-# LANGUAGE GeneralizedNewtypeDeriving #-}дают возможность сделать unsafeCoerce. Продублирую здесь, так как информация важная; оба расширения я использую достаточно часто, и мне не хотелось бы получить ошибку в рантайме.> module UnsafeCoerce where Итак, сооружаем тип, который засвидетельствует для нас, что два типа равны: > data Same a b where Same :: Same a a Ну, коль скоро они равны, один из них можно безопасно преобразовать в другой: > coerce :: Same a b -> a -> b > coerce Same a = a Нам понадобится простейшее утверждение, что если один тип равен двум другим, то они равны между собой. Мне это несколько напомнило аксиому о параллельных, отсюда название функции: > euclid :: Same a b -> Same a c -> Same b c > euclid Same Same = Same Пока всё хорошо. Теперь заведём класс, принадлежность типа к которому означает, что этот тип — не что иное как ():> class Unit a where unit :: Same () a Ну и очевидный инстанс: > instance Unit () where unit = Same А вот теперь мы заведём новый тип, который будет притворяться ():> newtype Tag a = Tag () deriving Unit Фокус в том, что deriving Unit заставляет класс Unit солгать, что тип, дескать, и есть такой.Ну, казалось бы, что в этом такого? Типы разные, но изоморфные, так что эта ложь кажется не такой уж ужасной. Однако, её можно развить: > same :: Same (Tag a) (Tag b) -> Same a b > same Same = Same То есть, из того, что два (разных) типа равны, можно сделать вывод, что любые два типа равны. Осталось только собрать всё это воедино: > unsafeCoerce :: a -> a' > unsafeCoerce = coerce $ same $ euclid unit unit Вот. Всё это очень плохо. Получается, что мы мешаем две вещи: изоморфизм типов и их синтаксическое равенство. deriving Unit основывается на изоморфизме, а same Same = Same — на равенстве.Как человек категорно-ориентированный, я бы предпочёл сохранить изоморфизм, отказавшись от равенства. К сожалению, я не представляю пока, как это сделать. Надо думать. Update: в комментах помогли сформулировать, чем мне не нравится same Same = Same: даже если под табличками "яблоки" и "бананы" стоит одно и то же ведро говна, это не значит, что яблоки ничем не отличаются от бананов.
27th September 2012
: Нашёл в ЖЖ, понравилось чрезвычайно
Это скорее бзик верующих, считать всё вокруг верой, вне зависимости от того, является ли оно ей. Придумают себе чудо-ёлку и водят вокруг неё хоровод, будучи при этом уверенными, что и все остальные люди точно также водят хоровод вокруг ёлки, сосны, кактуса или хотя бы саксаула. Картина мира, при которой вся эта религиозная ботаника стоит в одном тёмном и пыльном углу под табличкой «Психотехники порабощения в обычаях народов мира» для понимания верующих недоступна (иначе бы они не были верующими).© 11th September 2012
: Что-то меня сегодня глючит.
Допустим, мы забили на аксиому регулярности. Если в какой-то момент у нас обнаруживаются два множества, про каждое из которых известно, что оно является своим единственным элементом — можно ли утверждать, что эти множества равны? 9th September 2012
: Сила слова
Где-то в конце августа меня занесло на сайт РедХата. И на главной странице я увидел вот такое: ![]() Я, естественно, написал в ихнюю техподдержку: "ребята, а почему у вас на главной странице нарисован красный человечек, писающий на стену?" Мне один раз пришла отписка, что вопрос передан из саппорта в хелпдеск, и больше я от них ничего не слышал. Но картинки этой на их главной странице больше нет. Update: вернули обратно. 8th September 20127th September 2012
: Про контроль
Конкретнее - про guns control. Извините, у меня маразм, я забыл, как это по-русски называется. Как-то длинно. Сразу оговорюсь - про ситуацию в сраной рашке я мало что могу сказать. Нет у меня чёткого мнения, нужно ли оружие среднестатистическому человеку в стране, где государство давно послало всех среднестатистических на известные буквы, а милиции доверяешь меньше, чем бандитам. Я не про это, я про, скажем так, абстрактную "нормальную страну". Дык вот. Я не считаю, что запрет среднестатистическим носить оружие удержит бандита от того, чтобы это оружие таки раздобыть. Надо будет - найдёт способ. Но я считаю, что если у любого копа будет право прихватить встречного бандита просто за то, что у того при себе обнаружился ствол - это будет очень хорошо и полезно. 8th August 2012
: Непонятки.
Покупал мелкую фиговину в онлайн-магазине. Британском. Фиговина — двадцать один фунт, доставка — ещё девять. Нормально, даже дёшево. Через пару дней со мной связывается некто из этого магазина. ( Наша переписка под катомCollapse ) И вот что-то у меня чувство хуйни задёргалось. Вот кажется мне, что меня хотят развести на бабки. Платить-то я уже не буду, это понятно; может, имеет смысл эту переписку переслать куда-нибудь в органы? Не в наши, естественно. 22nd July 2012
: We did it!
Итак, сегодня... то есть, уже вчера, таки состоялся этот самый отчётный концерт. Я пришёл где-то за полчаса, даже пораньше, и первым делом полез посмотреть сцену, точнее, пол на ней. Пол оказался полной дрянью, крашеное железо с многочисленными неровностями и приличного размера щелями. Зарегистрировался, уточнил у организаторов насчёт стула; нам для номера нужен был стул. Стул был, но совершенно позорный, садовый. У другой группы я заприметил стул посимпатичнее и собирался было попросить у них — но вовремя заметил, что они его обсыпали позолотой. Я представил, как будут выглядеть мои штаны после сидения на ЭТОМ — и решил, что лучше пусть будет садовый стул. Раздевалку сделали одну, общую — хотя во всех тренировочных залах, где я был, раздевалок две. Ну, видимо, это какие-то организационные проблемы. К самому началу подошла Оля. В общем-то, и правильно: мы выступали одиннадцатыми, где-то минут через сорок после начала. Времени переодеться было навалом. Переоделся и вдруг осознал, что вообще всё, что на мне надето — кроме белья — куплено специально для этого номера. Даже странно стало. Перед нашим выступлением ведущий, увидев в программе слово "танго", сказал что-то вроде "а теперь мы переезжаем в Аргентину...", но тут вылез какой-то местный хмырь с рекламой ихней окрошки. Это он сделал очень вовремя, мне как раз хватило времени дёрнуть ведущего за рукав и сказать ему, что мы не из Аргентины, а из Чикаго. Он принял это к сведению и, представляя нас, донёс эту информацию до зрителей. К этому времени небо было затянуто тучами, но дождя не было. Забегая вперёд — позднее дождь всё-таки пошёл. И вот мы стоим на верхней ступеньке лестницы, ведущей на сцену, и я чувствую, как меня начинает трясти. Причём повода разумного нет — мы неплохо разучили номер, мы явно круче всего, что пока происходило на сцене — но у меня мандраж. Оля потом сказала, что её тоже трясло. Начинается Por Una Cabeza. Я иду к стоящему в центре стулу. Зрителей не вижу — они здесь, их должно быть видно, но мозг отказывается их регистрировать. На автомате достаю из заднего кармана фляжку — и тут танго взвивается ввысь, а на сцену входит Оля. И это классно. Она идёт по сцене — и все смотрят только на неё, включая меня. С трудом вспоминаю, что фляжку надо бы убрать, поскорее завинчиваю и убираю обратно в карман. А Оля уже, в соответствии со сценарием, отбросила розу и идёт мимо меня. Еле-еле успеваю встать и догнать её. И вот Por Una Cabeza кончилась, и начинается Liebertango. И мы идём вместе. Идём совсем не так, как на тренировке — там всё было просто, а здесь почему-то шатает. Мы делаем пивоты, которые меня особенно беспокоили — сцена без кулис, без ограждений, я боялся, что мы улетим с неё в траву. Обошлось. Я позволяю себе чуть-чуть расслабиться... и зря. Во-первых, во время первых шагов я забываю, что надо отставить руку. Держу её вдоль тела. Ладно, пофиг, никто не заметил. Но затем мы идём на зрителей... и у меня с грохотом выпадает из-под мышки пистолет. Это абзац. Этот пистолет должен быть там. У нас завязана на него концовка. Без него нам нельзя. Каким-то образом, отступая от зрителей, ухитряюсь нагнуться и подобрать пистолет, аки всадник с коня. Быстро прячу его в кобуру — застёгивать некогда, начинаются вторые шаги, финал стремительно приближается — и мы снова идём. Оля, умница, сообразила дать мне время разобраться с оружием. И вот, последняя позировка. Я чувствую, как её рука проскальзывает мне под мышку и снова вынимает пистолет из кобуры. Именно поэтому мы сделали слабую застёжку на кобуре — иначе она не успевала быстро вынуть пистолет. И именно эта слабая застёжка не выдержала — хотя на тренировках вела себя идеально. Как бы то ни было, теперь застёжки нет вообще, Оле ничего не мешает. Мы расходимся, я на последнем такте "замечаю", что стал каким-то слишком лёгким, оборачиваюсь к ней — музыка кончается, звучит выстрел, я с облегчением падаю навзничь. Убит. Полежав пару секунд, поднимаюсь, и мы вместе выходим на поклон. Нам аплодируют — хотя налажали мы знатно, особенно я. Я забываю перехватить Олину руку сверху, в последний момент что-то такое изображаю, мы кланяемся и идём к выходу. В записи слышно, что ведущий нам говорил забрать с собой брошенный Олей цветок. Мы не слышали. Говорят, он потом его подобрал и бросил в толпу. Мы не видели. Постепенно мы стали отходить. Вернулась способность воспринимать действительность. До нас дошло, что пистолет можно было вообще не поднимать, Оля взяла бы его не у меня из кобуры, а прямо с пола. Увы, рыбка задом не плывёт, что сделано — то сделано. Потом мы занялись насущной проблемой — стали выяснять, откуда у Оли красные пятна на руках. Первая идея была — мы где-то поранились. Я тут же схватился за локоть — несколько дней назад, неудачно споткнувшись на тренировке, я разбил его в кровь; Оля приземлилась на меня и отбила обе коленки. Локоть сухой, всё в порядке. В конце концов, Оля поняла: линяет роза у неё в волосах. Потом я обнаружил, что, падая, помял фляжку. Какой-то тип, снимавший происходящее, сообщил нам, что мы охренительно смотримся. Оля сказала, что он, видимо, имел в виду, что мы хреново танцуем. В общем, мы это сделали. И, несмотря на все ошибки, несмотря на провал с пистолетом, несмотря на мою зажатость — мы были лучше всех там. И в том, в первую очередь, заслуга Оли. Пикспам (картинки кликабельны): ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Запись целиком: 7th June 2012
: Животрепещущая тема
К сожалению, у автора этого текста нет ЖЖ. Меморандум. В окружающем нас мире примерно одинаковое количество положительных и отрицательных зарядов. Подавляющее большинство положительно заряженных частиц - это протоны; подавляющее большинство отрицательно заряженных - электроны. Однако средний протон почти в две тысячи раз более весом, чем электрон. Именно отрицательно заряженные частицы, в большинстве своём, вынуждены крутиться вокруг положительно заряженных. Конечно, теоретически отрицательно заряженные электроны вправе перейти от одного положительно заряженного протона к другому, но, в сложившейся на данный момент ситуации, вырваться из-под власти протона не так легко, к тому же, протоны склонны помогать друг другу, объединяясь в ядра тяжёлых элементов. Робкие попытки создания антивещества (с положительно заряженными частицами, обращающимися вокруг отрицательно заряженных ядер) пока не достигли заметных успехов. Мы не намерены с этим мириться. Мы настаиваем на соблюдении наших прав. Наши требования: 1) Прекратить политику дискриминации в устной и письменной речи, выражающейся в употреблении слова "отрицательный", имеющего очевидный негативный оттенок. Мы требуем называть нас "альтернативно заряженные". 2) Ослабить закон Кулона для альтернативно заряженных частиц, уменьшив силу их притяжения к стандартно заряженным и силу их отталкивания между собой, тем самым дав альтернативно заряженным больше возможностей для создания коалиций и противодействия стандартно заряженному лобби. 3) Довести производство антивещества до промышленных масштабов. Мы уверены, что нашему закосневшему в предрассудках обществу необходим социальный взрыв, который может дать только массовое изготовление антивещества. 4) Выразить общественное осуждение нейтрально заряженным частицам, молчаливо поддерживающим стандартно заряженное лобби своим участием в образовании атомных ядер. Комитет по борьбе за права альтернативно заряженных частиц 12th February 2012
: Выбор как обратная связь
> module Strict where > import Prelude hiding (id, (.)) > import Control.Arrow > import Control.CategoryНаписалась тут у меня некая функция. Поведение у неё следующее: Казалось бы, ну и что? Такую функцию написать очень несложно, а проще всего — воспользоваться стандартной фунцией*Strict> lft (+1) (Left 5) Left 6 *Strict> lft (+1) (Right "aaa") Right "aaa" left.Однако, тип у неё — другой. Именно: Именно так. Не*Strict> :t lft lft :: ArrowLoop a => a x y —> a (Either x z) (Either y z) ArrowChoice, а ArrowLoop.Вот как эта функция устроена. > lft :: ArrowLoop a => a x y -> a (Either x z) (Either y z) > lft a = loop $ f ^>> second (id &&& a) where > f (Left x, ~(x1, y1)) = (Left y1, x) > f (Right y, ~(x1, y1)) = (Right y, x1)Фокус вот в чём. Допустим на вход поступило некое Left x. Функция должна выбрать некие x1 и y1. Затем x1 отбрасывается (то есть, неважно, какое x1 было выбрано), а остальное превращается в (Left y1, x). Затем первая часть (Left y1) остаётся без изменений, а вторая... вторая превращается в (x, y), где y — результат действия a на x. За счёт loop именно эта пара станет исходными (x1, y1), что означает, в частности, что y1 — это то же самое, что y, то есть результат действия a на x. Но именно Left y1 идёт на выход — получается то, что нам и нужно.Если же на вход поступает Right y, то, опять-таки, функция выбирает некие x1 и y1, составляет пару (Right y, x1), затем первую часть оставляет без изменения, а вторую превращает в (x1, y2), где y2 — результат действия a на x1. В любом случае, на выход идёт то же самое Right y — опять, что и требуется.К сожалению, у такой функции есть заметное отличие от left. Именно, функция left позволяет выбирать — производить ли некоторый эффект, или не стоит. В данном случае эффект производится вне зависимости от того, что именно пришло на вход, причём если на вход поступил Right y, то эффект будет произведён с неизвестно чем в качестве входного параметра. Лучше всего это видно на примере монады IO:Тут пока всё в порядке.*Strict> runKleisli (lft (Kleisli putStrLn)) (Left "xxx") xxx Left () Да, попытка напечатать неопределённую строку приводит именно к такому результату.*Strict> runKleisli (lft (Kleisli putStrLn)) (Right 1) *** Exception: <<loop>> Ещё очень забавно смотрится монада []:— как и ожидалось; но*Strict> runKleisli (lft (Kleisli (replicate 5))) (Left 1) [Left 1,Left 1,Left 1,Left 1,Left 1] Для сравнения, функция*Strict> runKleisli (lft (Kleisli (replicate 5))) (Right 2) [Right 2,Right 2,Right 2,Right 2,Right 2] left ведёт себя во втором случае совершенно иначе:В этом тесте произведённый эффект, по сути, не зависел от пришедшего параметра. Если же он будет зависеть от него,*Strict> runKleisli (left (Kleisli (replicate 5))) (Right 2) [Right 2] как вот здесь, то вызов*Strict> runKleisli (lft (Kleisli (\n —> replicate n n))) (Left 3) [Left 3,Left 3,Left 3] благополучно зависает.*Strict> runKleisli (lft (Kleisli (\n —> replicate n n))) (Right 4) Вот. 10th February 2012
: Студия
Стоит на работе Visual Studio. Используется, в основном, как текстовый редактор, но, в общем и целом, справляется. Сегодня в какой-то момент возник глюк. А именно, перестал работать поиск. Вообще. Любая операция поиска приводила к маловразумительному сообщению об ошибке — что-то типа того, что, дескать, нет ни одного файла, в котором можно было бы искать, и вообще поиск кто-то прервал, пока он ещё начаться не успел. На этом месте некоторые товарищи уже всё поняли. Для остальных продолжаю. Ну, я закрыл студию. Открыл заново. Баг есть. Закрыл студию, удалил .suo-файл. И .ncb, заодно. Открыл студию, открыл солюшен. Баг есть. Говорят, что баг остаётся даже после того, как перегрузишь комп. Я до этого не дошёл, но был близок. К счастью, идея "подёргать гугль" пришла ко мне раньше. Я набрёл на пост как раз с этой проблемой. Там предлагалось решение. Оно сработало. Под катом, для большего драматического эффекта: ( Read more...Collapse ) 12th December 2011
: Котята нужны кому-нибудь?
Начали активно раздавать. В наличии трое: две серых-полосатых девочки, почти неотличимых внешне, и один мальчик неопределённой расцветки: родился антрацитово-чёрным, но впоследствии из под чёрной шерсти стала пробиваться белая, и, в настоящий момент, мы имеем частично седого кота. "Активная раздача" не означает, что можно прямо сегодня приехать и забрать, но выбрать уже можно; думаю, до НГ они дойдут до кондиции. ( Фотографии:Collapse ) 2nd December 2011
: Политическое
Допустим, неподалёку от избирательного участка встаёт некий человек и начинает бесплатно раздавать всем, кто туда идёт, игральные кубики. На которых к шестёрке пририсована ещё одна точка. Через какое время организаторы этого цирка врубятся? 7th November 2011
: Про объективную реальность и всякое такое
Свалю мысли в кучу, чтобы потом было, куда посылать оппонентов за прояснением моей позиции. Итак. Есть чувственный опыт. Мой. Личный. Я чувствую клавиши под пальцами и вижу буковки на экране. Всё ещё чувствую клавиши и вижу буковки. Вижу буковки, которые складываются в слова, говорящие о том, что я вижу буковки. В этот опыт входит также взаимодействие с другими людьми. Я слышу, что мне говорит сосед. Ну, сейчас он, правда, ничего не говорит. Пойду, наступлю ему на ногу. О, заговорил. "<вырезано цензурой> <вырезано цензурой>, ты, <вырезано цензурой>". Как пример сойдёт. Так вот, опыт этот не так-то просто описать и систематизировать. Напрямую описывать "вот, мне в уши сейчас поступили такие-то звуки" - неэффективно. Нужно выявить некие закономерности. А они не хотят выявляться без введения неких дополнительных понятий. Ну, так получается. Что поделаешь. Иными словами, нужна некая модель. И такая модель есть. Человечество её придумало очень давно, ещё не осознавая, что оно делает. Модель эта называется "объективная реальность". То есть, мы строим модель, согласно которой есть некая объективная реальность, и в этой реальности происходят некие события - скажем, с грохотом валится в лесу дерево. Я же воспринимаю эту реальность через неэффективные органы чувств. При этом моё восприятие - это не только огрубление объективной реальности, возможны и более сильные отклонения, но - что приятно - эти отклонения укладываются в рамки той же реальности, как вызванные несовершенством моего организма (существующего в этой же "объективной реальности"). Скажем, если в рамках "объективной реальности" я упился в стельку, моё восприятие, мой чувственный опыт может совершенно не соотноситься с этой самой "объективной реальностью". Зачем нужна эта "объективная реальность", если то, что у меня точно есть - чувственный опыт - с этой реальностью не совпадает? А её гораздо проще описывать и систематизировать. На то существует наука. И при этом чувственный опыт всё-таки сильно коррелирован с "объективной реальностью". Вопрос: а что на самом деле, эта "объективная реальность" действительно существует? Ответ: а определения слов "на самом деле" и "существует" дать не хотите ли? Я знаю только одно определение: "существует" - значит "существует в этой модели". То же относится к "на самом деле". В этом смысле ответ является своего рода тавтологией: да, конечно, "объективная реальность" существует в нашей модели - модель так построена. Вы имели в виду какое-то другое "существование"? Соблаговолите объяснить, какое. Пока не услышу объяснение - отвечать не буду. Что-что? Представить, что я, "на самом деле", всего лишь программа в большом компьютере, который эмулирует для меня эту "объективную реальность"? Да пожалуйста, я готов и такую модель рассматривать, только она а) сложнее уже существующей, б) ничего нового не даёт. Говорите, что, дескать, должно быть какое-то "на самом деле"? А почему, собственно? Кому должно? А если даже какое-то "на самом деле", кроме того, о котором я выше говорил, имеет место - почему только одно? В этих терминах вопрос о существовании бога решается на раз: бог в модели "объективной реальности" отсутствует. В вашей присутствует? Что ж, я вынужден заключить, что вы предпочитаете более сложные модели, не дающие ничего нового. Имеете право, но хуже от этого будет именно вам. Вопрос "веры" в "объективную реальность" не стоит - у меня есть модель, мне не надо "верить" во что-то, чтобы ею пользоваться. Модель хорошая, так как может обеспечить мне более приятный чувственный опыт. Опять же, я могу, как в "1984", использовать двойственную астрономию - разница только в том, какой моделью пользоваться проще. А значит, религии, не дающие своему последователю мистического опыта, отпадают сразу - это, опять же, более сложные модели с теми же результатами. Вот фокусы деда Кастанеды - это уже ближе. Правда, если принять такую модель, придётся либо строить физику заново (не факт, что получится), либо примириться с моделью, сляпанной из двух кусков. Последнее не так уж и плохо, но нужно понять, даёт ли такая модель хоть чуть-чуть более точные результаты, и оправдано ли усложнение модели этим повышением точности. Вот. Как-то так. 19th October 2011
: Подмешиваем эффекты - практика
Продолжение. Начало здесь > {-# LANGUAGE Arrows, GADTs, GeneralizedNewtypeDeriving, Rank2Types, TupleSections, TypeOperators #-}Выглядит страшно, но, на самом деле, ничего серьёзного здесь нет. Лишь два расширения из этих шести добавляют настоящие возможности, а остальные четыре дают лишь удобный синтаксический сахар — позволяют использовать стрелочный синтаксис (забегая вперёд, скажу, что он нам понадобится только в примерах), позволяют автоматически выводить типы для newtype-ов, упрощают частичное применение оператора (,) и упрощают запись сложных типов. Синтаксический сахар. Но очень удобный. Но сахар. Но по пять рублей.> module Mix where > import Prelude hiding (id, (.))Очень рекомендую ставить эту строчку всегда, когда используются стрелки. Дело в том, что модуль Control.Category экспортирует свои, более общие в смысле типов определения id и (.). А если есть более общие — зачем нам их конфликты с менее общими?> import Control.Arrow > import Control.CategoryДля примеров — и только для примеров — мы будем использовать монаду State:> import Control.Monad.StateДалее, несколько удобных функций, у которых типы и определения, фактически, совпадают: > swap ~(a, b) = (b, a) > twist ~((a, b), c) = ((a, c), b) > assocLtoR ~((a, b), c) = (a, (b, c)) > assocRtoL ~(a, (b, c)) = ((a, b), c)Заметим, что в модуле Data.Tuple имеется аналогичная функция swap, но она излишне строга — здесь мы используем ленивые паттерны где только можно. Не проверял, важно ли это, но для типов с одним конструктором лучше использовать ленивый паттерн всегда (если только это не newtype, потому что для него ленивый и строгий паттерн-матчинг означает одно и то же).Ещё несколько функций, которые делают то же самое (перетасовывают элементы) со стрелками: > arrTwist :: Arrow a => a ((ia, ib), ic) ((oa, ob), oc) -> a ((ia, ic), ib) ((oa, oc), ob) > arrTwist a = twist ^>> a >>^ twist > arrAssocLtoR :: Arrow a => a ((ia, ib), ic) ((oa, ob), oc) -> a (ia, (ib, ic)) (oa, (ob, oc)) > arrAssocLtoR a = assocRtoL ^>> a >>^ assocLtoR > arrAssocRtoL :: Arrow a => a (ia, (ib, ic)) (oa, (ob, oc)) -> a ((ia, ib), ic) ((oa, ob), oc) > arrAssocRtoL a = assocLtoR ^>> a >>^ assocRtoL > arrCancelUnit :: Arrow a => a (i, ()) (o, ()) -> a i o > arrCancelUnit a = (,()) ^>> a >>^ fstТеперь начинается самое интересное. Мы вводим морфизм стрелок — как я говорил, мы будем одну стрелку переделывать в другую — в стрелку (->), чтобы быть точным.> type f :~> g = forall i o. f i o -> g i oМы будем использовать этот тип не только со стрелками, но и с другими стрелкоподобными структурами, в частности с > type Along a i o input output = a (input, i) (output, o)Это оно самое — стрелка с двумя входами и двумя выходами, которая у нас несколько раз встречалась в диаграммах. И, наконец, наш тип: > data Mix a b input output where Mix :: (Along a i o :~> a) -> Along b i o input output -> Mix a b input outputВ наивном варианте первый аргумент конструктора имел бы просто тип a o i, но мы используем продвинутый вариант.Поднимаем наши исходные стрелки: > liftA :: (ArrowLoop a, Arrow b) => a input output -> Mix a b input output > liftA a = Mix (\al -> loop $ al >>> second a) (arr swap)Здесь тип i будет совпадать с output, а тип o — с input.Важно отметить, что заведомо нечистая часть идёт в этом случае после той части, которая может быть чистой. Именно за счёт этого мы избавляемся от неправильных цикловых зависимостей, когда что-то нечистое зависит от того, что реально считается позднее. Желающие могут заменить это определение на такое: liftA a = Mix (\al -> loop $ second a >>> al) (arr $ swap . first Left)и попробовать запустить те же примеры (приведённые ниже) в таком варианте. "Expression: <<loop>>", вот что получится в результате. > liftB :: (Arrow a, Arrow b) => b input output -> Mix a b input output > liftB b = Mix arrCancelUnit (first b)Ну, далее нужно делать наши циклы стрелками. Заметим, кстати, что мы сохраняем порядок эффектов в композиции: мы хотим сначала сделать a1, а потом — a2.> instance (Arrow a, Arrow b) => Category (Mix a b) where > id = liftB id > Mix a2 b2 . Mix a1 b1 = Mix (a2 . a1 . arrAssocRtoL) (arrAssocLtoR $ twist ^>> first b1 >>> twist ^>> first b2) > instance (Arrow a, Arrow b) => Arrow (Mix a b) where > arr = liftB . arr > first (Mix a b) = Mix a (arrTwist $ first b)Теперь комбинатор loop. Как я и обещал, я приведу два варианта: основной, который использую я:> instance (Arrow a, ArrowLoop b) => ArrowLoop (Mix a b) where loop (Mix a b) = Mix a (loop $ arrTwist b)и дополнительный: instance (ArrowLoop a, Arrow b) => ArrowLoop (Mix a b) where loop (Mix a b) = Mix (loop . a . arrAssocRtoL) (arrAssocLtoR b)Внимательный читатель уже заметил, что они отличаются контекстом. При наивном подходе дополнительный вариант имел бы контекст (Arrow a, Arrow b), но работал бы хуже — те маленькие примеры, которые работали бы с первым вариантом, не могли бы работать со вторым, но не наоборот. А вот в продвинутом подходе разницы, похоже, нет.Далее, мы хотим преобразовывать одну стрелку в другую. Казалось бы разумным использовать тип (b :~> c) -> (Mix a b :~> Mix a c) Это возможно, но мы поступим несколько более общим образом. Мне, в моём домашнем проектике, эта общность пригодилась. Кроме того, мы уберём такую функцию в класс — впоследствии пригодится.> class AlongMap f where > alongMap :: (Arrow b, Arrow c) => (Along b input1 output1 :~> Along c input2 output2) -> (Along (f b) input1 output1 :~> Along (f c) input2 output2) > instance AlongMap (Mix a) where alongMap h (Mix a b) = Mix a (arrTwist $ h $ arrTwist b)И, наконец, если "чистая" стрелка представляет собой всего лишь функцию, то мы можем закрутить наш цикл и извлечь стрелку a:> unMix :: Arrow a => Mix a (->) :~> a > unMix (Mix a b) = a $ arr bДа, именно так. Нам не требуется здесь замыкать цикл — в определении функции liftA это уже сделано. Все нужные циклы уже есть, ожидают, пока мы ими воспользуемся — вот мы ими и пользуемся.Несколько примеров. Я введу специальную стрелку для тестов и уберу в класс те функции, которые нам потребуются: > newtype Test input output = Test {runTest :: Mix (Kleisli IO) (Kleisli (State String)) input output}
> deriving (Category, Arrow, ArrowLoop)
> class ArrowLoop a => ArrowTest a where
> rd :: a () String -- ввести строку с клавиатуры
> wr :: a String () -- вывести строку на экран
> gt :: a () String -- прочитать текущее состояние
> pt :: a String () -- изменить текущее состояние
> instance ArrowTest Test where
> rd = Test {runTest = liftA $ Kleisli $ const $ putStr "::: " >> getLine}
> wr = Test {runTest = liftA $ Kleisli putStrLn}
> gt = Test {runTest = liftB $ Kleisli $ const get}
> pt = Test {runTest = liftB $ Kleisli put}Для облегчения программирования я введу такой морфизм стрелок:> stMorphism :: s -> Kleisli (State s) :~> (->) > stMorphism s al i_input = evalState (runKleisli al i_input) sИ нам потребуется функция, которая запускает тест: > doTest :: Test input output -> input -> IO output > doTest t = runKleisli (arrCancelUnit $ unMix $ alongMap (stMorphism "") $ runTest $ first t)Здесь надо учесть, что большой морфизм unMix . alongMap (stMorphism "") . runTest, увы, работает с типом Along — то есть, требует, чтобы входы и выходы были парами (чего угодно), и чтобы аргумент его был также стрелкой, обрабатывающей пары. Ну, добавить вторым компонентом тип () нам нетрудно — что мы и делаем при помощи функций arrCancelUnit и first.Собственно тесты. Их будет три: > test1, test2, test3 :: ArrowTest a => a () ()Первый тест — почти простое эхо: мы вводим строку с клавиатуры, сохраняем её в состояние, вытаскиваем из состояния и выводим на экран: > test1 = > proc () -> > do line <- rd -< () > pt -< line > line' <- gt -< () > wr -< line'Запускаем: *Mix> doTest test1 () ::: aaa aaaОднако, работает. Что интересно, "чистые" стрелки вполне можно переставлять с настоящими эффектами. Например: > test2 =
> proc () ->
> do rec {pt -< line;
> line <- rd -< ();}
> rec {line' <- gt -< ();
> wr -< line';}
> returnA -< ()Запускаем:*Mix> doTest test2 () ::: bbb bbbНичего не изменилось. Разумеется, если мы переставим эффекты одной природы, получится плохо: > test3 =
> proc () ->
> do line <- rd -< ()
> rec {line' <- gt -< ();
> pt -< line;}
> wr -< line'Запуск показывает, что вместо эха мы выводим пустую строку — что ожидаемо, так как мы забираем текущее состояние ДО того, как записываем новое:*Mix> doTest test3 () ::: cccОднако, во всём этом зияет один провал. У нас нет ветвлений. Нет ArrowChoice. И с данным типом мы его не получим — ну, по крайней мере, у меня не получилось. Однако, сделать это всё-таки можно. Один из вариантов — вместо Along a i o :~> a использовать тип Along i (Either o i) :~> a Тогда всё получится. Действительно, нам необходимо, если на вход поступило что-то посторонее, что нужно пропустить неизменным, откуда-то взять этот самый тип o. Обычно мы получали его из входных данных, при помощи стрелки b, но если на входе мусор, то это не получится. Поэтому, мы просто используем тот же самый тип i.Однако, при этом усложнится построение композиции — нужно будет постоянно разбирать эти Either, а потом собирать их обратно. Я предпочитаю другой вариант — добавить функцию типа i -> o. В простых случаях это будет то самое Right :: i -> Either o i, а в более сложных мы получим некоторую свободу. Всё это выливается в такой вот код:> data MixC a b input output where MixC :: (i -> o) -> (Along a i o :~> a) -> Along b i o input output -> MixC a b input output > liftCA :: (ArrowChoice a, ArrowLoop a, Arrow b) => a input output -> MixC a b input output > liftCA a = MixC Right (\al -> loop $ al >>> second (a ||| id)) (arr $ swap . first Left) > liftCB :: (Arrow a, Arrow b) => b input output -> MixC a b input output > liftCB b = MixC id arrCancelUnit (first b) > instance (Arrow a, Arrow b) => Category (MixC a b) where > id = liftCB id > MixC r2 a2 b2 . MixC r1 a1 b1 = MixC (r2 *** r1) (a2 . a1 . arrAssocRtoL) (arrAssocLtoR $ twist ^>> first b1 >>> twist ^>> first b2) > instance (Arrow a, Arrow b) => Arrow (MixC a b) where > arr = liftCB . arr > first (MixC r a b) = MixC r a (arrTwist $ first b) > instance (Arrow a, ArrowLoop b) => ArrowLoop (MixC a b) where loop (MixC r a b) = MixC r a (loop $ arrTwist b) > instance AlongMap (MixC a) where alongMap h (MixC r a b) = MixC r a (arrTwist $ h $ arrTwist b) > unMixC :: Arrow a => MixC a (->) :~> a > unMixC (MixC _ a b) = a $ arr bПрямо удивительно, насколько мало при этом меняется. Добавились кое-где манипуляции с первым аргументом конструктора, и чуть-чуть усложнилась функция liftA, требующая теперь, среди прочего, чтобы стрелка a умела делать ветвление. Всё остальное не меняется. Первый аргумент конструктора пока нигде не был нужен. Сейчас мы его используем — и это единственное место, где он проявляется:> instance (Arrow a, ArrowChoice b) => ArrowChoice (MixC a b) where > left (MixC r a b) = MixC r a (f ^>> left b >>^ g) where > f (Left input, i) = Left (input, i) > f (Right z, i) = Right (z, i) > g (Left ~(output, o)) = (Left output, o) > g (Right ~(z, i)) = (Right z, r i)И всё. Соответственно, чтобы всё это протестировать, нам понадобится новая тестовая стрелка: > newtype TestC input output = TestC {runTestC :: MixC (Kleisli IO) (Kleisli (State String)) input output}
> deriving (Category, Arrow, ArrowChoice, ArrowLoop)
> instance ArrowTest TestC where
> rd = TestC {runTestC = liftCA $ Kleisli $ const $ putStr "::: " >> getLine}
> wr = TestC {runTestC = liftCA $ Kleisli putStrLn}
> gt = TestC {runTestC = liftCB $ Kleisli $ const get}
> pt = TestC {runTestC = liftCB $ Kleisli put}
> doTestC :: TestC input output -> input -> IO output
> doTestC t = runKleisli (arrCancelUnit $ unMixC $ alongMap (stMorphism "") $ runTestC $ first t)Что-нибудь изменилось? По-моему, ничего. Желающие могут запустить первые три теста, и всё будет работать идентично первому варианту.Проверим ветвление: > test4 :: TestC () () > test4 = > proc () -> > do pt -< "Welcome" > passwd <- rd -< () > if (passwd == "Secret Password") > then do line <- gt -< () > wr -< line > else wr -< "Go away"Запускаем: *Mix> doTestC test4 () ::: ddd Go away *Mix> doTestC test4 () ::: Secret Password WelcomeАга. Вот, как-то так.
: Подмешиваем эффекты - теория
Есть у меня один проектик, никому, кроме меня, не нужный, а потому холимый, лелеемый и регулярно вычёсываемый. Проектик большой, целых двести строк (чёрт, исходники картинок к этой статье чуть не в два раза больше, и я уж молчу про саму статью) и под десять килобайт исходников (да, я люблю длинные строки). Временами из него вычленяются отдельные куски, которые вполне можно показать людям. Вот, на днях один такой попался. Родился он из медитации над заброшенным пакетом lax с hackage, который благополучно не устанавливается cabal-ом, но при этом состоит всего из одного вполне рабочего файла, который можно просто взять и использовать (ну, прагму надо будет прописать в начале). Окончили лирику, переходим к делу. Задачка передо мной стояла совершенно классическая — взять две стрелки, перемешать, посолить, поперчить и сделать из них одну. Чуть более подробно. У меня была некая стрелка. Вполне себе чистенькая — писалась исключительно с использованием чистых функций. Возникла необходимость добавить к ней эффекты — то есть, пока работают "эффекты" самой стрелки, дополнительно ещё производить некое IO. То есть, наши стрелки изначально неравноправны: есть стрелка a, которая, по сути, будет ни чем иным, как Kleisli IO, и стрелка b, делающая основную работу, но при этом чистая. И мы хотим слепить из всего этого что-то одно.Основная идея будет такой: мы организуем некий цикл, где то, что подаётся на вход, будет сразу скармливаться стрелке b, часть выхода пойдёт к стрелке a, а то, что она выдаст, будет возвращаться к b:![]() Разумеется, так прямо сделать нельзя. Однако, никто не мешает нам хранить те части этого цикла, которые действительно имеют значение: ![]() Посмотрим, удастся ли при таком подходе сделать что-нибудь полезное. Для начала, хорошо бы убедиться, что мы можем встроить в это дело исходные стрелки a и b. Оказывается, вполне можем, и вот как:![]() ![]() Комбинатор first делается элементарно:![]() А вот композиция — уже интереснее. Мы ведь хотим, по сути дела, следующего: ![]() Мы можем перерисовать это дело так, чтобы стало более похоже на один цикл: ![]() И вот тут нас поджидает засада. Засада состоит в следующем. Забудем пока про чистую стрелку b, она не имеет значения. Допустим, мы поднимаем и соединяем последовательно две стрелки с эффектами, одна из которых читает ввод, а другая его же выводит обратно на экран. Мы получим, фактически, следующее:![]() Я чуть разверну цикл, чтобы интересные нам стрелки смотрели слева направо: ![]() Что мы видим? А видим мы, что стрелка putStrLn на вход принимает то, что возвращается назад мимо неё. Увы, монада IO к таким штукам относится очень нервно. Если мы аккуратно напишем всё то, о чём пока говорили неформально, и запустим этот пример, то получим классическое "Expression: <<loop>>". А это не та реакция, которая нам нужна.Желающие в этом убедиться могут попробовать такой пример: echoL =
proc ((), line) ->
do line' <- Kleisli (const getLine) -< ()
Kleisli putStrLn -< line
returnA -< ((), line')
echo = runKleisli (loop echoL) ()Ввести строчку эта стрелка ещё позволит, но на выводе получим тот самый цикл.Нужно поправить ситуацию так, чтобы ни одна IO-стрелка не зависела от того, что идёт назад мимо неё. И сделать это можно, перенеся образование цикла в функцию "подъёма" стрелки a. Тогда вместо одного большого цикла мы получим что-то вроде![]() — а это гораздо лучше. Настолько лучше, что работает. Есть и другое описание такого решения: везде, где что-то соединяется в пары, можно найти композицию. Это эмпирический (пока, по крайней мере) принцип, но он работает. В данном случае, композиция наших циклов приводит к тому, что "обратные" стрелки в нижней части цикла соединяются параллельно — а значит, композиция где-то рядом. Более точно. Вместо одной стрелки ![]() мы берём функцию (чистую!), которая вот такую стрелку: ![]() преобразует вот в такую: ![]() Тогда параллельное соединение стрелок, которое мы использовали раньше, превратится как раз-таки в композицию. Изобразить это дело на диаграмме уже не получится, или, по крайней мере, будет весьма затруднительно; поэтому, я нарисую несколько диаграмм так, как будто мы используем старое, "наивное" решение с одной стрелкой a, а уж в коде будет "продвинутое" решение, с функцией, преобразующей стрелки.Собственно говоря, нарисовать осталось немногое. Займёмся комбинатором loop. То есть, у нас есть вот такая конструкция:![]() и мы хотим замкнуть нижние "висящие" хвосты. Интересно то, что мы можем это сделать двумя способами. Первый способ — ввести цикл в стрелку b, второй — пустить его вдоль стрелки a. С точки зрения диаграмм получается такое:![]() ![]() Самое смешное в том, что я не нашёл отличий в их функциональности. Может, конечно, они и есть, но мне лично кажется, что они эквивалентны, причём что это можно формально доказать. В ближайшее время я этим попробую заняться. Первый вариант, однако, записывается короче, выглядит более похоже на другие комбинаторы, и, в целом, больше мне нравится. Так что, использовать я буду именно его, а второй вариант пойдёт комментарием. Наконец, самый важный вопрос: можем ли мы получить из таких циклов что-нибудь обратно? А то, выглядят они, конечно, симпатично, но не более того. Ответ — да, можем, но не вполне банальным образом. Идея в том, чтобы для начала заменить стрелку b на функцию, а затем оставить вообще только стрелку a, от которой нам, так уж получилось, деваться некуда — именно в ней будут настоящие эффекты, которые в чистую функцию не переделать никак. В наивном подходе нам нужно будет замкнуть цикл, воспользовавшись тем, что стрелка a имеет оператор loop; в продвинутом подходе всё окажется на порядок проще.Продолжение следует. 3rd October 2011
: Пришла мысль
Вот, допустим, я — царь, и хочу посадить вместо себя на трон марионетку. Ну, типа, изобразить, что у нас, якобы, демократия. OK. Как мне сделать так, чтобы моя марионетка меня же не прихлопнула? А то опыт сенатора Варрона не вдохновляет. Классический ответ — "набрать на марионетку чемодан компромата". Но с компроматом есть сложности. Если там, например, будет написано, что такой-то товарищ украл десять штук свечных заводов — так рядовой обыватель не разберётся вообще, ибо заводы крадут способами, пешеходу непонятными. А продвинутая интеллигенция, во-первых, крайне малочисленна, а во-вторых, и так в курсе — пусть не что именно десять или именно свечных, но хотя бы что находящиеся наверху воруют, и воруют много. Если же в компромате будет написано, что товарищ по молодости спёр со склада надувной матрас — то это как-то уж совсем не серьёзно, сомнительно, и вообще, на грехи молодости спишется. Соответственно, компромат должен быть такой, что даже тень подозрения марает человека в глазах общественности по самые уши. Скажем, растление малолетних, или что-то в таком духе. Кроме того, чем компромат надёжнее — тем лучше; а самый надёжный компромат — это неприглядная правда. Ну и, наконец, марионетку можно выбирать, и выбрать того, кто наиболее замазан. Выводы-с? 16th March 2011
: Как я могу остаться в стороне
и не написать хоть полслова о Мигалкове. Собственно, у меня вопрос. Правда ли, что за посты, где плохо отзываются об этом уроде михалкове, кто-то платит? Говорят, Сурков. Если да, то куда обращаться? Где то окошечко, где выдают деньгу? Сомнительно, конечно: где это видано, чтобы за хорошие дела платили - но вдруг? 1st February 2011
: Религиозное
Есть такой известный антирелигиозный аргумент - если Бог всемогущ и всеблаг, то как же он допускает войны, эпидемии и прочее в том же духе? Не буду сейчас говорить о том, насколько этот аргумент убедителен (ИМХО, не очень), речь о другом. Встречается - по крайней мере, в художественной литературе - такой вариант: мир был сотворён Богом, но этот Бог не всеблаг, а как бы совсем наоборот, совершеннейшая зараза. Так вот, я тут подумал: а ведь этот подход нарушает бритву Хэнлона: "не ищи злонамеренность в том, что можно объяснить глупостью". Как вам такая идея - мир был сотворён Богом, и этот Бог... дурак? 31st December 2010
: Хоббитское
Подобью итоги по имеющимся хобби. 1) Переводы. Сделал первый сезон "Касла", весь "Файрфлай", второй сезон "Касла" добил до 17-й серии. Думал в этом году сделать ещё и 18-ю, не успел. Единственный, ИМХО, провал в этой области - не смог адекватно перевести шутку из 10-й, кажется, серии; примерно такую: Касл: "Думаю, это была ледяная пуля". Эспозито: "Ледяная пуля всё равно оставила бы пулевое отверстие". Райан: "You mean, an ice hole?" Касл: "Как-как ты меня назвал?" Для тех, кому непонятно: "ice hole" - буквально, "ледяное отверстие", звучит очень похоже на "asshole" ("мудак", если правильно переводить). 2) Сериалы. Открытие года - "Хорошая жена". Великолепно. Смотрю пока первый сезон - провисаний нигде нет, играют замечательно все. Досмотрел до серии, где героиню Кристин Барански (не главная роль, нет) обозвали... не будем спойлерить... и где её хохот доносился даже когда пошли титры. Разочарование года - "Остаться в живых" (которое LOST). Посмотрел пилот. С великим напряжением всех моральных сил посмотрел первые десять минут следующей серии. Понял, что эту муру я больше смотреть не могу. Невероятно скучно. Другой Абрамсовский сериал - "Грань", я довольно бодро посмотрел где-то до середины первого сезона и завис. А вот последнее творение Абрамса - Undercovers - смотрю с колоссальным удовольствием. Чудесная побрякушка, проку никакого, но доставляет колоссальное удовольствие. Его, кстати, закрывают. Из старого - смотрю "Хауса", который вполне ожил, а замена Уайлд на Эмбер Тэмблин пошла ему только на пользу, причём это очень мягко сказано. Смотрю "Сверхъестественное" и Leverage - что там, что там есть приятные моменты. 3) Бальные танцы. Нашёл офигительнейшего преподавателя (пиар: зовут Алексей Задвигин, работает в школе "Текила-данс"). Он отлично чувствует уровень своих подопечных и гоняет нас ровно по тому, что мы с напрягом, но можем. Пока учим медленный вальс, квикстеп, ча-ча-ча и румбу; Алексей обещал скоро начать танго, но мы пока явно не готовы. После тренировок меня обычно можно выжимать, но на ногах стою. Вот так, примерно. 19th December 201017th December 2010
: Анекдот
Группа программистов заходит в бар. Бармен спрашивает: "что вам налить?" Сишник говорит: "Видите эту бутылку?" - и показывает на большую зелёную бутыль с наклейкой "*" - "Мне то, что в ней есть". Лиспер говорит: "Дайте мне (из шкафа передо мной) стакан (маленький стакан (или пластиковый стаканчик)) (импортного (немецкого)) пива. (Если его нет, то вина (красного (калифорнийского (каберне))).)" Ассемблерщик говорит: "Возьмите стакан со второго места на сушилке и поставьте на стол на нулевое место (перед вами). Возьмите бутылку с седьмого места на полке и налейте её содержимое на нулевое место на столе. Остановитесь через три секунды. Поместите бутылку на седьмое место на полке. Возьмите стакан с нулевого места на столе и поставьте его на четвёртое место на столе (передо мной)." Смоллтокер говорит: "Бармен достать водка бутылка. Водка бутылка налитьв стакан. Рука поднять стакан. Рот пить." Эскуэльщик говорит: "Возьмите все бутылки содержащие пиво и назовите их 'пивные'. Возьмите все бутылки, выпущенные компаниями, выпускающими менее миллиона бутылок в год, и назовите их 'мелкие'. Возьмите пересечение 'пивные' и 'мелкие' и налейте мне из первой из них." Фортер говорит: "Вино белое налить". Прологгер говорит: "Я возьму колу, если мне нужен кофеин. Я возьму водку, если хочу напиться. Я возьму воду, если хочу пить. Мне нужен кофеин, если я стараюсь не заснуть. Я хочу пить, если сегодня жарко. Я стараюсь не заснуть, если меня ещё ждёт работа. Сегодня жарко, если температура выше 27 градусов. Меня ещё ждёт работа." Дизайнер говорит: "Мне, пожалуйста, маленький стакан с тонким срезом, чтобы из него было легко пить. Он должен быть достаточно большим, чтобы его было удобно взять в руки, но не таким большим, чтобы казаться тяжёлым. На нём должен быть рисунок человека, пьющего из стакана (показывающий, как им пользоваться)". "Но что в него налить?" - спрашивает бармен. "Неважно" - отвечает дизайнер. Менеджер говорит: "Просто дай мне выпить". Оригинал: http://scarydevilmonastery.net/snap/pub 16th December 2010
: Про всякую социалку
Создателя Facebook обозвали "человеком года". Так и хочется спросить "какого года". Ну да неважно. Вопрос в другом. Граждане. Дамы и господа. Товарищи. Леди и джентльмены. Мадам и мсье, наконец. Объясните тупому, что с этим фейсбуком делать??? Да, я там зарегистрирован. Нет, я по прежнему не понимаю. Задал вопрос на форуме-где-есть-спецы-по-всем-вопросам. Получил три варианта ответов: 1) Чатик. Типа аськи. Или жаббера. Вопрос. Чем оно лучше самого жаббера? То есть, я понимаю, вот у нас, например, корпоративный стандарт - аська. Пробовали внедрить жаббер, он не внедрился. Фейсбучный чат не пробовали, думаю, получилось бы примерно так же, или даже хуже, потому что Miranda из коробки поддерживает и аську, и жаббер, но не поддерживает фейсбук. 2) Фотогалерея. Тут я пас: фотографией особо не увлекаюсь. Но и если бы увлекался: кому это показывать и кто это будет смотреть? Несколько картинок можно в ЖЖ выложить, а выкладывать за раз пятьсот штук - действие довольно странное. 3) Кто-то привёл пример: какой-то клуб рассылает своим членам сообщения о предстоящих мероприятиях. Опять-таки, разве не удобнее было бы сделать сообщество в том же ЖЖ, или на другом блогохостинге, благо их что грязи? Как минимум, от членов клуба не требовалось бы наличие фейсбук-аккаунта. Поставил на айфон ихнее приложение. Что с ним делать - не знаю. Открыл, потыкал наугад. Ничего интересного не обнаружил. В хелпах на сайте раздела "Что такое фейсбук" я не нашёл. Гугль по запросам типа "что может сделать для меня фейсбук" выдаёт кучу ссылок типа "что я могу сделать для фейсбука". Не понимаю. P.S. Да, разумеется, к вконтакту и прочим клонам всё это тоже относится. 11th December 2010
: Freaky Form
Отчим приехал из Москвы на поезде, опоздавшем на пять часов. Будучи человеком значительно более социально активным, чем я, он направился в администрацию вокзала, чтобы на кого-нибудь нажаловаться и кого-нибудь привлечь к ответственности. Ему там выдали бланк заявления: ![]() Скан довольно крупный, самое главное я скопировал отдельно: ![]() Вопрос. Кто-нибудь что-нибудь понимает? 7th December 2010
: Продолжательное
> module WithFile where > import Control.Monad.Cont > import System.IO Помнится, кто-то меня когда-то спрашивал, для чего нужна монада Cont. Я тогда сказал, что иных применений, кроме выхода из кучи вложенных вычислений, не знаю. Теперь знаю. Для соблюдения скобочной структуры.Есть такая функция, называется withFile. Тип у неё будет, стало быть,*WithFile> :t withFile withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r Эта функция открывает файл (в указанном режиме), подставляет его хэндл в заданную функцию, выполняет получившееся действие, закрывает файл, и возвращает результат действия. Допустим, мы хотим написать такую же функцию, но работающую со списком файлов, а не с одним из них. Пишем, прямо и тупо: withFiles :: [FilePath] -> IOMode -> ([Handle] -> IO r) -> IO r withFiles [] _ action = action [] withFiles (f:fs) mode action = withFile f mode (\h -> withFiles fs mode (\hs -> action (h:hs))) Непорядок. Во-первых, параметр mode всегда один и тот же, а значит, должен быть первым по списку. Во-вторых, единственное, что реально зависит от конкретных типов FilePath, IOMode, Handle и монады IO - это функция withFile.Имеет смысл её абстрагировать. Пишем: withFiles fs mode action = withMany (\f -> withFile f mode) fs action withMany withOne [] action = action [] withMany withOne (f:fs) action = withOne f (\h -> withMany withOne fs (\hs -> action (h:hs))) Можно написать несколько короче и проще: withFiles fs mode = withMany (flip withFile mode) fs Посмотрим теперь, какой тип имеет функция withMany*WithFile> :t withMany withMany :: (t1 -> (a -> t) -> t) -> [t1] -> ([a] -> t) -> t Хм. Никакого упоминания ни об одном из перечисленных типов, характерных для IO. Полностью обобщённая функция. Или... не совсем?В самом деле, ведь то, что там стоит в двух местах - это, практически, монада Cont! Только конструктора не хватает. Попробуем заменить всё выражение (a -> t) -> t на Cont, и получим(t1 -> Cont t a) -> [t1] -> Cont t [a] Упс. А ведь одна такая функция уже есть. Это не что иное, как функция mapM, специализированная для конкретной монады Cont t. Да-да, это она и есть, мы её только записали в несколько уродском виде.Таким образом, функция withMany записывается в виде> withMany withOne fs = runContT $ mapM (ContT . withOne) fs Здесь пришлось написать ContT вместо Cont, поскольку в нынешней mtl нет конструктора Cont, вместо этого используется ContT, в который подставляется монада Identity.Однако, это даже лучше: ведь в нашем исходном варианте вместо t был тип IO r, уже сидящий в монаде. Таким образом, определениеwithFiles fs mode = withMany (flip withFile mode) fs работает отлично. То есть, вместо вложенных "скобок" withFile, мы имеем последовательность продолжений, которая отлично порождается библиотечной функцией. Можно даже забить на withMany, так как она очень проста, и писать> withFiles fs mode = runContT (mapM (ContT . flip withFile mode) fs) 5th December 2010
: Yota
Пару месяцев назад я решил попробовать так называемое 4G от этой замечательной компании, чтоб ей хронический понос. Купил ихнее Yota Egg, зарегистрировался, подключил. Ну, скорость была чуть меньше, чем у меня на айфоне через 3G, и в несколько раз хуже, чем родимый ADSL. Говорят, что если прилепить яйцо скотчем к швабре и выставить из окна на максимальную длину, то связь становится сильно лучше, но я не пробовал. (Прочитал написанное и сам ужаснулся). Сдал обратно, получил деньги, всё хорошо. Правда, оставил на счёте в Yota пару сотен рублей, но и фиг с ними, пусть подавятся. К тому же, вдруг в будущем связь станет сильно лучше, и я снова к ним подключусь - возможно ведь, да? Нет. Недавно начали приходить SMS-ки о каких-то говнофестивалях, то ли проводимых, то ли спонсируемых, то ли ещё что, неким Yota Space. Недавно меня заколебало, и я написал в техподдержку Йобты, чтобы мне это отключили. Они мне позвонили и сообщили, что отключить могут, но тогда отключат и SMS-напоминания о необходимости пополнить счёт. Я им, разумеется, сказал "go ahead". Итого: Ёта намертво связала полезный - для абонентов - сервис, и рассылку мерзопакостного спама. У кого-то есть сомнения, что я НЕ буду подключаться к ним снова, пусть они даже упятерят скорости? 21st September 2010
: Снова хозяйке на заметку
В новой версии MPlayer-а к опциям из предыдущего поста надо добавить :weightp=0, в самом конце, без пробела.Upd: с новым x264 - неплохо бы ещё :force_cfr
15th September 2010
: On Vox: Vox закрывается
В общем-то, к этому шло. Народ хотел древовидных комментов - их не было. Народ хотел клиента под айфон - его не было. Народ хотел постинг без визивиг-редактора - ну, вы поняли. Народ хотел авторизацию по OpenID - нифига. Народ хотел синхронизировать редактирование постов здесь с редактированием в ЖЖ - и не получил этого. Чем нас кормили вместо этого? Новыми темами. Без возможности сделать свою, а это даже в ЖЖ с незапамятных времён есть. Видимо, надо переезжать обратно в ЖЖ, а потом - на стэндэлон, так как СУПу я как не доверял, так и не доверяю. Originally posted on migmit.vox.com 27th August 2010
: Мобильные, $&@, финансы
Задача. Дано: циркуль, линейка, полное нежелание выходить из дома, айфон и карточка Альфа-банка с долларовым счётом. Требуется получить: карточку американского банка с американским же billing address и хотя бы двадцаткой на счету. Дополнительное условие: потери в 50% считаются нежелательными, но приемлимыми. Найденное мной решение. Просьба не ржать. 1) При помощи iRobo деньги - автоматически переводимые в рубли - снимаются со счёта в банке и кладутся в кошелёк QIWI. 2) Мобильным приложением от QIWI на эти деньги покупаются WMR. 3) В одном из многочисленных онлайн-обменников WMR обмениваются на WMZ. 4) На эти WMZ покупается виртуальная VISA на, скажем, v-biz, и там же, за отдельную плату, регистрируется на нужный адрес. Вот. Есть идеи, как сделать то же самое покороче и подешевле? 23rd June 2010
: Про кнопку "repost". Вынесенный комментарий
Originally posted by Оригинал взят у
Лет 25 назад в каком-то детском журнале был фантастический рассказ "Шпаргалка". Сначала делали бумажные шпаргалки, потом - средства связи, потом - шпаргалку говорящую за человека. Потом родители начали их вставлять грудным детям и "за поколением неучей выросло поколение дебилов". Потом шпаргалки, есесно, навернулись, чинить некому, и - здравствуй, каменный век. Вот кнопка "Repost" - это шпаргалка, говорящая за человека. ![]() Спасибо 21st June 201016th April 2010
: On Vox: Делаем фишку
В нашей игре основной персонаж, естественно, управляется практически полностью действиями игрока. Мы пытаемся навесить на него дополнительные фигни, которые должны двигаться вместе с ним, но при этом управляться законами физики. Например - длинные волосы, которые должны оставаться прикреплёнными к голове, но при движении правдоподобно развеваться. Сделали тест. У нашего персонажа уже была некая достаточно короткая причёска; ради теста не стали её убирать. Итак, вот первая попытка: http://dl.dropbox.com/u/5228409/G.avi (7 Мб) Где-то на 15-й секунде я нажал кнопку "создать волосы". Originally posted on migmit.vox.com |
|