Паттерн использования такой: идёт поток ивентов, среди них нужно учитывать только последний по какому-то id-полю. То есть пока я думал сделать LRU-кеш, переписывая по этому самому id-полю значения, при удалении из LRU считать объект "конечным", также при окончании ивентов всё, что внутри LRU считать конечным.
Драйвер редиса возвращает `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") ``` Вопрос: вам не кажется это "слишком"? Я пока еще не настолько просто манипулирую типами в голове, чтоб ощутить всю ситуацию, хорошо хоть в целом могу медленно прости по шагам по коду, но есть ощущение, что что-то здесь не так.
haskellcast.com второй эпизод, с Доном Стюартом. Очень интересно и доступно, в расслабленной манере про себя и свою работу.
О зарплатном диапазоне не знаю. Могу предположить диапазон $30-$60 долларов в час в зависимости от опыта и навыков (при работе через oDesk), но лучше узнавать у самого работодателя. Находится он, кстати, в США, потому существует не очень удобная разница во времени. В остальном, как по мне — работа прекрасна.
Если кто-то заинтересован — шлите мне свои детали на k-bx@k-bx.com вместе со ссылками на гитхаб, резюме и прочие вспомагательные материалы. Спасибо!
[0]: thoughtleadr.com