Мыслеформы -

топ 100 блогов jdevelop06.07.2010 забавно, с тайпклассами весьма удобно делать совершенно дикие вещи

ну например вместо того, чтобы передавать в функцию еще пачку функций-трансформаторов, делаешь себе чуток data/newtype, говоришь правильные типы явно в сигнатуре функции или в выражении - и оно самостоятельно работает

а код при этом уменьшается раза так в полтора-два

автоматический деривинг классов - вообще космическая вещь, никакой жабе в упор не снилось

получается такое себе квазиметапрограммирование - достаточно определить операции над типами, и компилятор сделает все остальное по месту пользования

например

newtype Result a = Result { getLJResult :: Either LJError a } deriving (Show)

makeErrorStr ::  String -> Result (Either LJError a)
makeErrorStr = Result . Left . SimpleError

makeError ::  LJError -> Result a
makeError = Result . Left

makeResult :: a -> Result a
makeResult = Result . Right

class ResponseTransformer a b where
    transform :: ParseResult String a -> b

applyResultP :: (ResponseTransformer a b) => Result (ParseResult String a) -> Result b
applyResultP = applyResultP' . getLJResult
    where
        applyResultP' (Left err) = makeError err
        applyResultP' (Right s) = makeResult $ transform s

runRequest :: (ResponseTransformer a b) => LJRequest -> CustomResponseParser String a -> IO ( Result b )
runRequest request responseParser = do
    curl <- CU.initialize
    applyResultP <$> parseResponse _customObjectFactory _customObjectDAO .  extractResponse <$> 
        CU.do_curl_ curl "http://www.livejournal.com/interface/flat" curlOptions
    where
        _customObjectFactory = customObjectFactory responseParser
        _customObjectDAO = customObjectDAO responseParser
        curlOptions = makeRequest request : CU.method_POST
        makeRequest = CurlPostFields . DL.map makeParamNV . params
        makeParamNV (RequestParam name value) = name ++ "=" ++ value

newtype ChalString a = ChalString { getChStr :: a }

instance ResponseTransformer ( ChalString String ) (Maybe String) where
    transform (simpleMap, _, _) = DMP.lookup "challenge" simpleMap
            
prepareChallenge :: String -> IO (Maybe (String, String))
prepareChallenge password = do
    handleResponse <$> (runRequest request (CRP noFactory noUpdate) :: IO (Result (Maybe String)))
    where
        handleResponse src = handleResponse' $ getLJResult src
        handleResponse' (Left err) = Nothing
        handleResponse' (Right res) = makeChallengePair <$> res
        request = makeRequest [("mode","getchallenge")]
        noFactory :: String -> Maybe ( ChalString String )
        noFactory = \_ -> Nothing
        noUpdate = \_ _ _ _ -> Nothing
        makeChallengePair chal = ( chal, hashcode chal )
        md5Pass = MD5.md5sum $ BStr.pack password
        hashcode chal = MD5.md5sum . BStr.pack $ chal ++ md5Pass


по факту расширение формата парсинга делается через определение инстанса ResponseTransformer, навроде как

instance ResponseTransformer LoginResponseData LJLoginResponse where
    transform (simpleMap, enumMap, objectMap) = undefined -- TODO get parts of response from the map

loginExt :: LJLoginRequest -> IO ( Result LJLoginResponse )
loginExt request =
    prepareChallenge ( password request ) >>= DM.maybe emptyResponse login'
    where
        emptyResponse = return ( makeError NoChallenge )
        login' (chal, auth_response) = 
            runRequest request' (CRP loginObjectFactory loginObjectUpdater) :: IO (Result LJLoginResponse)
            where
                params = DL.concat [
                        [
                            ("mode","login"), 
                            ("user",user request), 
                            ("auth_method","challenge"),
                            ("auth_challenge",chal),
                            ("auth_response",auth_response)
                         ],
                         DM.maybe [] ( makeTupleSArr "getmoods" .  show ) ( moods request ),
                         guard (menus request) >> makeTupleSArr "getmenus" "1",
                         guard (pickws request) >> makeTupleSArr "getpickws" "1",
                         guard (pickwurls request) >> makeTupleSArr "getpickwurls" "1"
                         ]
                request' = makeRequest params
                makeTupleSArr = ( return . ) . (,)


в сухом остатке получаем, что основное действие выполняется внутри

runRequest request' (CRP loginObjectFactory loginObjectUpdater) :: IO (Result LJLoginResponse)

где указание IO (Result LJLoginResponse) включает в работу аццкую типодробилку и на выходе применяются правильные тайпклассы к правильным объектам

Оставить комментарий

Популярные посты:
Архив записей в блогах:
Из боевой секты одного отправили обучаться у инопланетян. Так этот тип после возвращения, протянув руку создал свое новое пространство с отдельными полыми планетами, причем воинов он туда отправил в виде потока темных частиц вместе с процессом созидания, и они прям на создаваемых частях ...
В начале 1960–х гостем на одной радиопередаче, посвященной военной тематике, стал московский сапожник Иван Румянцев, инвалид войны. Он рядовым солдатом дошел до Берлина и был ранен в голову в последнем бою войны, во время штурма рейхстага. Оказавшись перед микрофоном, Иван внезапно ...
Кофе с сыром - любимый завтрак, а когда сыр такой вкусный, лучше завтрака нет
Средь зарослей сорного клена, ни троп, ни дорожек лишь тлен дыхание смерти и шорох истории, слёз и поэм. Здесь всё позабыто, веками, налет паутины и мха и я затаивший дыханье иду, сам не зная куда... Часть четвертая. 1. Каширин Николай Павлович 1913-1964. 2. Безымянная. 3. ...
Чтоб больше ничего не терять — решила добавлять дату, Ну и чтоб было ясно, что это не сейчас. Я пытаюсь вжиться в этот город изнутри, впитывать его запахи, неумолкаемый шум океана и крики чаек. Глядя на движение волн, легко себе представить, что они могут поглотить все на своем пути, ...