← All posts tagged Haskell

kb

Посоветуйте, кто пользовался, хороший LRU для хаскеля (или как искать). При том, что скорее всего придётся немного допилить его API под конкретную нужду (а может взять и свой небольшой написать?).

Паттерн использования такой: идёт поток ивентов, среди них нужно учитывать только последний по какому-то id-полю. То есть пока я думал сделать LRU-кеш, переписывая по этому самому id-полю значения, при удалении из LRU считать объект "конечным", также при окончании ивентов всё, что внутри LRU считать конечным.

kb

Вопрос к читавшим Functional Data Structures Окасаки. Вы читали вникая в доказательства или нет? То есть, я сейчас прочитал до места, где сложность очередей доказали через Banker's и Physicist Method'ы, и осознал, что либо мне нужно перечитывать всё с самого начала (а перед этим желательно почитать много разных примеров попроще по доказательству при помощи амортизации), либо забить и читать доказательства поверхностно, а внимательно читать только код.

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

Крайне нравится "Type Inference and Optimization for an Impure World" (правда, я пока где-то 70 из 240 страниц осилил). Рассказ о хаскеле-подобном языке Disciple, отличающимся наличием изменяемых переменных и их аннотациями в виде "регионов" и "эффектов" на уровне типов (тоже с выводом).

cs.anu.edu.au

kb

И почему людям приходит в голову делать API по типу hedis (драйвер для редиса)? У них на каждую операцию ответ вида `Redis (Either Response a)`, где Response — это такая смесь, которая заодно может содержать ошибку (`Response = Error | MultiResponse | ...`). Ну естественно, нормальному человеку лень писать вокруг этого обвязку (точнее, "она уже в планах, уж теперь-то точно"). В результате вот такой ошибки вида "оно не вставляется" я и занимался получасовым дебагом, блин (причем, непонятно, баг ли это в хедисе или где-то у меня).

kb

Проект, в котором я с недавнего времени работаю, ищет еще одного haskell-программиста. Суть проекта: native advertisement [0] (на сайте есть ролик), на хаскеле в нём написан веб-сервер, раздающий рекламу (js-файл), а также разные вспомагательные сервисы. Также часть сервисов написана на Python/Flask, но ими будут заниматься отдельные люди.

О зарплатном диапазоне не знаю. Могу предположить диапазон $30-$60 долларов в час в зависимости от опыта и навыков (при работе через oDesk), но лучше узнавать у самого работодателя. Находится он, кстати, в США, потому существует не очень удобная разница во времени. В остальном, как по мне — работа прекрасна.

Если кто-то заинтересован — шлите мне свои детали на k-bx@k-bx.com вместе со ссылками на гитхаб, резюме и прочие вспомагательные материалы. Спасибо!

[0]: thoughtleadr.com

kb

О да! Песочницы кабала в случае неразрешения конфликтов предлагают выставить `--max-backjumps`, и это даже работает! В результате отложил план по обновлению библиотек на еще попозже (может, вместе с переездом на ghc 7.6).

kb

Мда. Долго мучался (с попыткой описать типы внутри своих where-функций), пока не нашёл ScopedTypeVariables, как ответ на свои мучения. Всё же, считаю, что они долджны быть частью стандарта и в книжках описываться. Интуиция для них, как по мне, и так вполне сильная.