← All posts tagged code

kb

Драйвер редиса возвращает `Redis (Either Reply a)`. При операции `HGET` возвращает, соответственно, `Redis (Either Reply (Maybe ByteString))`, типа значения по ключу может и не быть. Программист для упрощения пишет сначала специальную функцию `redis'`, которая заворачивает ответ редиса в EitherT-based монаду, чтоб соединять подобные вычисления и возвращать первую неудачу:

```
newtype Redis' a = Redis' (EitherT Redis.Reply Redis.Redis a) deriving (MonadIO)
instance Monad Redis' where
    return = Redis' . EitherT . return . Right
    (Redis' m) >>= f = Redis' $ m >>= \rv -> unwrapRedis' $ f rv
redis' = Redis' . EitherT
```

Таким образом, теперь, если вы получаете ответ `Redis (Either Reply a)`, вы его преобразуете в

```
Redis' (EitherT Reply Redis a)
```

и можете соединять подобные вычисления в do-блоке типа:

```
do
    res <- redis' $ Redis.hget foo bar
    res2 <- redis' $ Redis.hget baz zab
```

и вычисление остановится на первом возврате ошибки.

Далее. Программисту необходимо по кучке значений сделать `HGET` и вернуть это как-то внутри кортежа, поскольку мы внутри новой монады `Redis'` -- завернуть результат в неё. В случае, если значения хоть по одному ключу не существует, хочется вернуть `Nothing` для всех. Потому создаётся новая монада:

```
newtype HashFields a = HashFields (MaybeT Redis' a)
    deriving (Functor, Monad)
instance Applicative HashFields where
    pure = return
    (<*>) = ap
```

описывающая вычисления типа `Redis'`, которые могут вернуть неудачу. Пишется новая функция

```
hashField = HashFields . MaybeT . redis'
```

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

```
getField :: ByteString -- ^ Key
         -> ByteString -- ^ Hash field name
         -> HashFields String
getField key field = fmap toString $ hashField $ Redis.hget key field
```

Также напишем функцию, которая "запустит" наше вычисление:

```
getRedisFields :: forall a. HashFields a -> Redis' (Maybe a)
getRedisFields (HashFields f) = runMaybeT f
```

И теперь лёгким движением руки мы можем сделать что-то вроде:

```
getRedisFields ((,,,,,) <$>
    getField k "foo" <*>
    getField k "bar" <*>
    getField k "baz" <*>
    getField k "zab" <*>
    getField k "rab" <*>
    getField k "oof")
```

Вопрос: вам не кажется это "слишком"? Я пока еще не настолько просто манипулирую типами в голове, чтоб ощутить всю ситуацию, хорошо хоть в целом могу медленно прости по шагам по коду, но есть ощущение, что что-то здесь не так.

kb

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

class A(object):
    def foo(self): pass
class B(A):
    def foo(self): pass
b = B()
b.foo

разрулило неправильно. При переходе на b.foo пошло сразу на A.foo почему-то. Короче, всё равно придется внимательно следить, видимо.

kb

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

Также есть двойные кавычки, в которых бэкслеш \ является обычным символом во всех случаях, кроме как если после него стоит один из символов (‘$’, ‘`’, ‘\’ или ‘!’), тут он, всё же, эскейпит. Удивительное решение сделать зависимость экранирования от контекста, ну и ладно. Bash:

kost@k-bx:~$ echo "tfoo"
tfoo
kost@k-bx:~$ echo "\tfoo"
\tfoo
kost@k-bx:~$ echo "\\tfoo"
\tfoo

А теперь, по ошибке забыв что у меня zsh, узрел, что там они тоже решили действовать своим, особенным способом. Я не читал документацию, но из экспериментов так и не понял принцип работы:

~ ➤ echo "tfoo"                                                                                                                                                    
tfoo
~ ➤ echo "\tfoo"                                                                                                                                                   
	foo
~ ➤ echo "\\tfoo"                                                                                                                                                  
	foo
~ ➤ echo "\\\tfoo"                                                                                                                                                 
\tfoo
~ ➤ echo "\\\\tfoo"                                                                                                                                                
\tfoo

Очень сложно со строками :)
p.s.: напоминаю, что "привычные" строки можно получить при помощи использования $'строка'

kb

tip of the day:
export PIP_DOWNLOAD_CACHE=$HOME/.pip_download_cache

Эта строчка сделает так, что pip будет качать все пакеты в эту директорию, а в случае, если там уже есть пакет -- не качать его, а брать оттуда. То есть в новом virtualenv теперь вам не будет больно сделать pip install -U ipython.

Интересно, почему это не по-умолчанию.

kb

Exercise 2.6. In case representing pairs as procedures wasn't mind-boggling enough, consider that, in a
language that can manipulate procedures, we can get by without numbers (at least insofar as nonnegative
integers are concerned) by implementing 0 and the operation of adding 1 as
(define zero (lambda (f) (lambda (x) x)))
(define (add-1 n)
  (lambda (f) (lambda (x) (f ((n f) x)))))
This representation is known as Church numerals, after its inventor, Alonzo Church, the logician who
invented the calculus.
Define one and two directly (not in terms of zero and add-1). (Hint: Use substitution to evaluate
(add-1 zero)). Give a direct definition of the addition procedure + (not in terms of repeated
application of add-1).


Для ленивых вот ответ:

(define zero (lambda (f) (lambda (x) x)))
(define one  (lambda (f) (lambda (x) (f x))))
(define two  (lambda (f) (lambda (x) (f (f x)))))

kb

Сволочи. Они взяли и реализовали cons/car/cdr через замыкания. То есть я и сам мог это сделать, а не догадался.

(define (cons x y)
  (define (dispatch m)
    (cond ((= m 0) x)
          ((= m 1) y)
          (else (error "Argument not 0 or 1 -- CONS" m))))
  dispatch)
(define (car z) (z 0))
(define (cdr z) (z 1))

kb

Текущий XMPPWSGI:

def application(environ):
    return [u"response", u"strings"]
    
    
типа пользователь пишет что-то, xmppwsgi-сервер создает environ
{'XMPP_JID': 'v.pup@ya.ru', 'MESSAGE': 'пишу боту'}

веб-сервер вызывает эту функцию и передает environ

то, что получит в ответ -- это ответ Васе, который он запостит. А теперь сама проблема:

если мы пишем свой juick -- когда вася пишет, к примеру, пост, надо ему ответить "message posted",
а еще его подписчикам ответить "vasya posted: 'ttttt'".

Но сами ответы в джаббер должен делать xmppwsgi-сервак (мы ничего не должны знать о самой посылке,
сообщений, мы только формируем что и кому, как в http-wsgi тебе все равно какой wsgi-сервер будет
отдавать тело ответа), а приложение должно только формировать поток сообщений вида
[["кому", "что"]...]. Вопрос, собственно. какой из вариантов?

Первый:


def application(environ):
    return [[u"response", u"message posted"], 
            [('one@ya.ru', 'vasya posted shit'),
             ('two@ya.ru', 'vasya posted shit'),]]

Второй:

def application(environ, message_queue):
    message_queue.append(('one@ya.ru', 'vasya posted shit'))
    message_queue.append(('two@ya.ru', 'vasya posted shit'))
    return [u"response", u"message posted"]
    
типа по ссылке list обычный ему давать. Плюсы первого -- очевидно. Минусы -- перелопатить весь код
и всегда возвращать два аргумента (хотя есть вариант, чтоб понимать и один и два аргумента).

kb

Кто бы мог подумать.
    def test_xmppwsgi_simple(self):
        environ = {'MESSAGE': 'PING', 'XMPP_JID': 'k.bx@ya.ru'}
        app = xmppflask.XmppFlask(__name__)
        @app.route(u'PING')
        def ping():
            return u'PONG'
        rv = app(environ)
        self.assertEquals(list(rv), ['PONG'])


(xmpp)kost@kost-narwhal:~/workspace/xmppflask$ nosetests
.
----------------------------------------------------------------------
Ran 1 test in 0.222s

OK

kb

Прикольно functools.partial http://docs.python.org/library/functools.html#functools.partial Это типа фигня, при помощи которой можно делать что-то вроде лямбд, только для уже готовых функций, да еще и переписывает поверху аргументы. К примеру:

>>> from functools import partial
... def a(b, c, d, e):
...     print b,c,d,e
... b = partial(a, b=10, c=11, d=12)
... b(d=14, e=13)
10 11 14 13

Мне нравится)

kb

(упс, еще раз, потому что %m а не %M :-) удобно забекапить pg_dump'ом много баз сразу одним махом в красивые имена скачать бесплатно

for DBNAME in db1 db2 db3 db4 db5; do pg_dump --host db --port 5432 --username postgres --format custom --blobs --verbose --file $DBNAME-$(date +%Y)-$(date +%m)-$(date +%d).backup $DBNAME; done

kb

………,………………..,……,………….,……,……………………
…,,..,………………………,……:,,::,,:,.,,,,:,.,,………,…,..,,….
..,..,,…..,……2illl▓▓███████████▓▓▓▓▓▓██▓………,..,,..
,…,..,…,.k▓█▓▓▓▓lBEnjjBllllllBjzv░1JTHtq8tL;..,…nll█▓…….,…,,
..,……v▓█M:..,::,,:;1JBt;,…,….q:……:vFll▓▓llEL…..█▓,……,…
,…,…..█,,,…░;……..;x,…….,:,,..,,:,,……..,░,,,…..█lM…….,,
…..,:n█▓…,..Lii███▓B1……..tj▓▓▓█████lM.,,……..j█▓;……
…W██▓w,,..,Y▓▓█████M…k██▓▓▓█▓lll▓▓v▓▓▓▓▓;.▓█▓…
..#ll█::vn▓▓▓▓g;J:..,.t█▓…………….,n▓▓glll▓▓▓▓Wl▓▓.wll█..
…zi█;:..,..,t▓7▓▓░2ll█▓:……,.X▓Bq..,………Bll▓▓█▓.El█3.F█lll
…..▓█1t;3█▓.,…Wl██,..,..f▓▓Mi█W,rB▓▓▓█lB;..▓█lBt█lK.:;█..
….,.f█▓W███▓▓7:..▒▓█M..tjBl▓██▓Bjzr▒█▓Bj██jY.,..,h█▓2..
…..,..j█.jll█Bl▓▒ll█fjllllll█llBIjj█6;:..▒▓▓▓████,l█▓……Bl█▓..,..
,……U█.;r█████▓▓▓█▓▓█▓▓█████Bj..C██W…..yi█;:…….
……..j█…█████████████▓lM:.k▓M..f▓█W,…,..lll█8:…..,…
,……N▓,..B██▓▓.█M1▓▓.,..j█….,;8l██▓W,…..░ll██r:…..,,..,,
……r▓▓…..▒ll▓▓▓██▓█▓▓██▓▓▓▓881xtll▓▓██▓I….,..,,..,…
……I█▓.6;,.░;,,,..,..;r░::v7::,..;YTn;xtll▓▓█▓▓n……….,,..,…..
…..w█▓..,JjlgglllBjkT:,..,………,,.,W▓▓▓▓H……………,,..,..,,..
,……U█▓r….,……,……,;rll▓l█▓▓▓▓t,..,……….,..,,..,..,,.,,….
………▒ll███▓▓▓▓███▓▓▓BBj::….,……,…..,,..,..,,..,..,,.,,…
,…,..,………….,…,……,……,..,…,..,……,……,…,……,……..