Archive for the ‘Програмування’ Category
Аргентинське танго
Одна из моих любимых начальных строчек в текстах танго такая: “Sin rumbo fijo, mi vida va…”. Она плохо переводится на английский, но примерно это означает: «Без направления, без определённой цели, протекает моя жизнь». На испанском это очень красиво.
В пошуках елітних тусовок я спробував займатись елітними танцями, а саме – танго. Ну що вам сказати, того що я шукав – не знайшов, зате прошарився у темі соціального танго, або, як його в основному називають, аргентинського танго. Виявляється, це доволі глибока тема, ключові аспекти якої я зараз оповім.
Близько третини танцюючих – айтішники
Цифра, звісно, дууже приблизна, але програмістів, тестерів, аналітиків, проджект менеджерів та тімлідів в київській тусовці доволі багато. Стандартно це пояснюють тим, що айтішники асоціальні і їм не вистачає спілкування з протилежною статтю, ось і приходять пообніматись. Ну це зовсім фантазії. А ось інший тезис “сидяча робота вимушує шукати альтернативну активну діяльність, наприклад, танці” уже ближче до реального положення речей, хоча все-рівно – не те.
Я скажу, що мені найбільше подобається – це імпровізації. Мій логічний розум полюбляє процес творчості, ось і знаходить спосіб її вираження у вертикальному вигляді. Незважаючи на те, що першопочаткова причина мого приходу в танго уже розтанула як марево, я тут залишусь ще на кілька років, саме по причині, що кожен наступний танець буде іншим і кращим ніж попередній. І тут краще, ніж в підпіллі.
Аргентинське танго – чому не бальне танго?
Під словом “танго” часто розуміють бальне танго, тому я пишу додаткове слово – “аргентинське” або “соціальне”. Давайте розберемось. Існує три види танго:
- аргентинське (воно ж соціальне),
- бальне,
- і танго-нуево – не можу знайти картинки, бо це щось занадто екстраординарне для Києва і надалі я про нього згадувати не буду.
Як видно по картинкам, перші два типи відрізняються якістю зображень і способом обнімання. Бальники дотикаються бедрами (читай, геніталіями), а ми – грудними клітинами (читай, цицьками). Це не єдина відмінність, але найбільш помітна неозброєним оком.
Соціальний елемент – мілонга
Як я вже згадував, весь танець – імпровізація партнера, де інструментом виступають ніжки та руки партнерши. І хоч це імпровізація, у неї все-рівно є алфавіт. І алфавіт настільки простий, що зовсім не обов’язково для танцю мати постійну одну і ту ж партнершу – береш будь-яку іншу, і танцюй на здоров’я, буквар всі проходили! Тому влаштовуються танго-дискотеки (називаються мілонги), куди можна прийти після важкого робочого дня та порозважатись зі знайомими дамами (або незнайомими). Етикет та атмосфера, звісно, зовсім відрізняються від традиційних дискотек в нічних клюбах, але кожному своє.
Власне це мене і зацікавило – не обов’язково мати постійну партнершу, ба, навіть краще мати багато різних. На уроках ротація в парах відбувається по 10 разів! На мілонгах – кожні 4 пісні.
Власне ці правила і провокують на знайомства. Хоч як би ти ненавидів людей, але без нових знайомств не обійтись. Навіть більше, якщо у тебе був страх знайомитись з дамами, то терапія через мілонгу – те що треба (якщо ти навчився крокувати хоча б).
Здавалось би, таке трапляється в будь-якій тусовці, але в цьому танго є ще один нюанс. Всі люди, котрі прийли сюди і залишилися, мали певні психологічні проблеми в дотанговий період. Так так, психологічно нормальні люди сюди приходять рідко. І всі в тусовці це розуміють, до кожного новачка відносятся з розумінням. В результаті ті, хто вже довго в танго, утворюють такий собі гештальт клюб. Вступивши в нього (читай, танцюючи з правильними людьми), ти будеш отримувати все нові і нові дози позитиву. (Звісно, без печалі в танго не обійтись – партнерша відмовила, спину защемило, забув зуби почистити, мілонга була непродуктивною або партнерша думала про когось іншого під-час танцю…)
Музика
Это вещество [окситоцин] обильно вырабатывается при сексе, объятиях, доверительных разговорах, молитве и медитации, хоровом пении… Наши проявления заботы о других людях повышают у них уровень окситоцина, возникает своего рода цепная реакция, когда люди заражаются друг от друга добротой.
Помните, еще у Павлова было: если каждый раз вкусно есть при определенной музыке, то и сама эта музыка становится более приятной — возникает ассоциация, условный рефлекс, и формируется нейронная сеть, которая за эту ассоциацию отвечает.
Ми танцюємо виключно під архаїчну аргентинську музику 30-х, 40-х та іноді 50-х. І то, не всю, а окремо вибраних оркестрів.
Можете вслухатись в цю музику, але сумніваюсь, що вам воно сподобається. Онлайн
- http://vk.com/audio?album_id=37908582
- http://music.yandex.ru/#!/users/lady-gaga-indie-child/playlists/1001
- http://music.yandex.ru/#!/users/lady-gaga-indie-child/playlists/1002
або офлайн https://dl.dropboxusercontent.com/u/11524542/Practice%20playlist.rar
І так само, як жіночі ніжки, ця древня танго-музика стає об’єктом фетишу та причиною для снобізму. Якщо при вас хтось випадково назве одне з прізвищ:
то оце значить він з нашого клюбу. А якщо замість них називатиме
Гардель | |
П’яццола | |
Otros Aires | |
або навіть “Мурка” |
то це не наш чувак, він або не танцює або електронщик або нуевщик.
Скільки грошей йде на танго?
Ця розвага доступна не усім класам населення. Якщо брати в середньому, то 50 грн в день (у вихідні – більше, у будні – менше). Усього ~1500₴ в місяць.
Ніби небагато, але з дорослішанням знадобиться ходити на семінари “авторитетних” тангеро, і там уже рахунок йде на євро. А якщо вкусити смак фестивалів та рандомних знайомств з іноземками, то додайте сюди ціну на білети, нові сукні, туфлі, костюми, сорочки, парфуми і тп і тп…
А також додайте сюди час, в який ви могли працювати і заробляти гроші, натомість просрали його на танцювання. В Аргентині навіть пісні складають, як люди протринькують життя на танго.
Пост не охоплює тему обіймів, миттєвих закоханостей та самої техніки крокування, бо мені це важко висловлювати словами. Також я не запостив жодного відео, бо відео не набагато зрозуміліше за слова. Сподіваюсь, читачі простять мене за відсутність лірики, але мені вже давно хотілось щось написати, ось я і написав про “наболіле” =)
А, і наостанок трошки жіночих ніжок на туфельках (в реалі на ніжках ще є пластир від мозолів та наступань ногами партнерів, але в інеті такого не знайшов):
Success story: novice haskeller moves console cursor with “netwire” FRP library
Hey, I’m definitely novice haskeller (just to point out: couple of weeks ago I’ve had understood State monad and monad transformers, before that I lived without state for almost 6 months), so I think it is just time to jump into FRP. Men say “FRP is cool” and “FRP is not slow” and “Conal Elliot, whah! beg beg beg”, so I, an novice hero have to tame that beast while the programming world starts to shift to Haskell!
So, what library for FRP sholud I use? Hm, lets examine Hackage…
Hackage: FRP
Ouch. Large choice make me cry ‘( Need something else.
StackOverflow: What’s the status of current Functional Reactive Programming implementations?
Okay, so reactive-banana
or netwire
. Seems like installing netwire
will be faster, because typing cabal install netwire
is faster than cabal install reactive-banana
.
Installed
Me, remind myself, why do I need FRP? Oh, yes, I’ve recently done a dozen of variations of console tetris and have not forgot all the pain for dealing with timers, state, input, game loop and laziness. I am adequate and I don’t want to rewrite that fully with such a new thing for me as FRP. So let’s take a small subtask – move console cursor on screen with keyboard (discovered System.Console.ANSI
recently) – and do that right way!
Let’s start with reading documentation. Hm… Sure… Wow… Ehh… Aha, here it is, an Example:
module Main where import Control.Monad.Identity (Identity) import Control.Wire import Prelude hiding ((.), id) import Text.Printf testApp :: Wire () Identity a Time testApp = timeFrom 10 main :: IO () main = loop testApp clockSession where loop w' session' = do (mx, w, session) <- stepSessionP w' session' () case mx of Left ex -> putStrLn ("Inhibited: " ++ show ex) Right x -> putStrLn ("Produced: " ++ show x) loop w session
Runnig this we have:
Produced: 12.8301619000001 Produced: 12.8371623000001 Produced: 12.844162700000101 Produced: 12.849163000000102 Produced: 12.854163200000102 Produced: 12.860163600000101 Produced: 12.866163900000101 Produced: 12.871164200000102 Produced: 12.880164700000103 Produced: 12.885165000000104 Produced: 12.893165500000103 Produced: 12.900165900000104 Produced: 12.905166200000105 Produced: 12.912166600000106 Produced: 12.918166900000106 Produced: 12.924167300000105
Wow! But what changes should I do, to stop it printing after e.g. 15 seconds? Diving to hackage docs… wackelkontakt
wtf?!.. Accum
… Analyze
… Effect
… It is definitely hard to find something, for which you do not know the name… Maybe that – when
? Let’s try. Other examples combine wires with composition, so do I:
testApp :: Wire () Identity a Time testApp = when (< 15) . timeFrom 10
Produced: 14.961283800000212 Produced: 14.968284200000213 Produced: 14.974284500000213 Produced: 14.982285000000212 Produced: 14.987285300000213 Produced: 14.993285600000213 Inhibited: () Inhibited: () Inhibited: () Inhibited: () Inhibited: ()
Cool! Not what I wanted, but output changed after 15 seconds. Win.
Reality
After examining example a bit carefully, I see the global loop. Documentation says nothing about neediness of global loop in wire program, so I can only assume – it is necessary. To make things simpler, let’s abstract from it:
control whenInhibited whenProduced wire = loop wire clockSession where loop w' session' = do (mx, w, session) <- stepSessionP w' session' () case mx of Left ex -> whenInhibited ex Right x -> whenProduced x loop w session
Now we can run wires like this:
main = control return (putStrLn . show) $ when (< 15) . timeFrom 10
14.966284000000254 14.969284200000255 14.974284500000255 14.978284700000255 14.981284900000256 14.985285100000256 14.990285400000257 14.995285700000258 14.999285900000258 Interrupted. *Main>
Do you see it? The output has stopped printing after 15 seconds gone! Great discovery, this will help me later, no doubt.
Keyboard
Dealing with console keyboard is not a hard task for a man, who wrote console tetris. Let’s reuse our keyPressed :: IO (Maybe Char)
foreign import ccall unsafe "conio.h getch" c_getch :: IO Char foreign import ccall unsafe "conio.h kbhit" c_kbhit :: IO Bool keyPressed = do isKey <- c_kbhit if isKey then Just <$> c_getch else return Nothing
Now we have to create wire from that function. How? Let’s read documentation. Hm.. Hm.. Hm-hm. As I see, the only wire creators are in Control.Wire.Wire
, and start with “mk
“. As I see, dealing with IO effects can only be done via mkGen
(but I’m not that sure)
mkGen :: (Time -> a -> m (Either e b, Wire e m a b)) -> Wire e m a bSource Construct an effectful wire from the given function.
Ok, maybe this way?
pressedKeyMaybe = mkGen isKey where isKey time () = do ky <- keyPressed return (Right ky, pressedKeyMaybe) -- strange, why do I use same -- function in the snd of the tuple?
Let’s test it:
main = control return (putStrLn . show) $ pressedKeyMaybe
Ha-ha! It doesn’t compile. Warum?
Couldn't match expected type `Identity' with actual type `IO' Expected type: Wire () Identity () b0 Actual type: Wire () IO () (Maybe Char) In the second argument of `($)', namely `pressedKeyMaybe' In the expression: control return (putStrLn . show) $ pressedKeyMaybe Failed, modules loaded: none. Prelude>
I’m lucky and this problem went off with simple replace stepSessionP
to stepSession
in control
function.
Nothing Nothing Just 'j' Nothing Nothing Nothing Nothing Nothing Just 'k' Nothing Nothing Nothing Nothing Nothing Nothing Nothing
Just to make it less verbose:
main = control return (putStrLn . show) $ when (/= Nothing) . pressedKeyMaybe
*Main> main Just 'h' Just 'e' Just 'l' Just 'l' Just 'o' Just ' ' Just 'n' Just 'e' Just 't' Just 'w' Just 'i' Just 'r' Just 'e' Interrupted. *Main>
It’s great time I live in, excuse me for my bad English and egocentrism.
(BTW, I give thanks to netwire author for the “wackelkontakt” mention in the doc. Because it is in fact an effectfull wire, so it can be used as example implementation:
wackelkontaktM :: (MonadRandom m, Monoid e) => Double -- ^ Occurrence probability. -> Event e m a wackelkontaktM p = mkFixM $ \_ x -> do e <- getRandom return (if (e < p) then Right x else Left mempty)
So my key stroke wire looks like this now:
pressedKeyMaybe = mkFixM $ \_ _ -> do ky <- keyPressed return (Right ky)
)
Counting keypresses
Next small subtask in my great mission is to count keystrokes. I know already that FRP is about accumulating events or smth like that, so I want to use accum wire for this task. My plan is:
countingWire = accum (+) 0 . when (/= Nothing) . pressedKeyMayb
That doesn’t work, because Maybe
s do not accumulate with (+)
. We have to convert them to integers:
toInt :: Int -> Wire m e a Int toInt v = mkPure (\_ _ -> (Right v, toInt v)) countingWire = accum (+) 0 . toInt 1 . when (/= Nothing) . pressedKeyMaybe main = control return (putStrLn . show) countingWire
(…pressing some keys, while running wire…)
*Main> main 0 1 2 3 4 5 6 7 8 9 Interrupted. *Main>
Astonishing result! But we can do better, because of <|>
operator. As I’ve understood, it looks like “or”, so we can have power:
countingWire = accum (+) 0 . (toInt (-1) . when (== Just 'h') <|> toInt 1 . when (== Just 'l')) . pressedKeyMaybe
But when we have power, we want more power! Let’s control two values simulationously:
cursorWire = accum (\(a,b) (c,d) -> (a+c, b+d)) (0,0) . ( pure ((-1), 0 ) . when (== Just 'h') <|> pure ( 1, 0 ) . when (== Just 'l') <|> pure ( 0, (-1)) . when (== Just 'j') <|> pure ( 0, 1 ) . when (== Just 'k') ) . pressedKeyMaybe
(Have you noticed, how toInt
was replaced with pure
? That was an “Aha” moment when studying wires)
*Main> main (0,0) (1,0) (2,0) (3,0) (3,1) (3,0) (3,-1) (2,-1) (1,-1) (0,-1) Interrupted. *Main>
Result
Now I am not that novice, that I was in the beginning, I am netwire-non-novice! Creating the cursor moving wire is now easy:
moveCursor = mkFixM $ \_ coords@(x,y) -> setCursorPosition y x >> return (Right coords)
And the result is:
main = control return (const (return ())) $ moveCursor . cursorWire
(Sorry, it's hard to show how I control the cursor with "hjkl" keys, simply believe me or try yourself)
I am pretty pleasant, that while we work with changing values, the whole system stays composable. I don’t know how to develop large systems with FRP architecure, but this small practical exercise gave me more than blind reading tons of reddit articles.
By the way, thanks for reading!
Post Scriptum
Have you ever noticed, that Windows console sends TWO keystrokes, when pressing arrow keys? Yeap, and if you worked with it, you may think it is headache with FRP. Suprisingly, no, with netwire it is simply a couple of compositions:
. edge ((/=) 224 . fromEnum . fromMaybe ' ') .
Putting this code before pressedKeyMaybe
in wire chain will filter only those intents, who had keycode 224
just a moment ago.
cursorWire = accum (\(a,b) (c,d) -> (a+c, b+d)) (0,0) . ( pure ((-1), 0 ) . when (== Just 'K') <|> pure ( 1, 0 ) . when (== Just 'M') <|> pure ( 0, (-1)) . when (== Just 'H') <|> pure ( 0, 1 ) . when (== Just 'P') ) . edge ((/=) 224 . fromEnum . fromMaybe ' ') . pressedKeyMaybe
Whole code – Github Gist: moving console cursor
Hometask
One more thing I’ve left to readers. Limit the cursor movement to screen rectanlge. So that we will not exit the screen area.