我正在寻找有关编写惯用的PureScript代码的反馈。下面的代码是从Twitter API读取的示例代码。辅助方法的签名是:
-- read consumer credentials from a config file
readConfig :: String -> Aff (Either String TwitterCredentials)
-- get the bearer authentication using the consumer credentials
getTokenCredentials :: TwitterCredentials -> Aff (Either String BearerAuthorization)
-- read the Twitter API using the bearer authentication
showResults :: BearerAuthorization -> String -> Aff (Either String SearchResults)
我的代码是:
main :: Effect Unit
main = launchAff_ do
let searchTerm = "PureScript"
config <- readConfig "./config/twitter_credentials.json"
case config of
Left errorStr -> errorShow errorStr
Right credentials -> do
tokenCredentialsE <- getTokenCredentials credentials
case tokenCredentialsE of
Left error ->
errorShow error
Right tokenCredentials -> do
resultsE <- showResults tokenCredentials searchTerm
case resultsE of
Left error ->
errorShow error
Right result ->
liftEffect $ logShow $ "Response:" <> (show result.statuses)
正如您所看到的,有很多嵌套的Either
语句,并且我多次调用errorShow
。您将如何编写此代码以使其更具可读性并可能消除代码重复?
答案 0 :(得分:1)
您可以将辅助函数从Aff (Either String a)
返回到ExceptT String Aff a
。 ExceptT
是一个monad转换器,它携带Either e a
代替值,这意味着您编译的代码看起来大致相同。但是在源代码级别,您可以忽略错误直到最后,从而获得可读性并减少重复。
如果您控制辅助函数的源代码,则直接重写它们:使用throwError
而不是返回Left
,使用Right
而不是返回pure
另一方面,如果您不控制帮助程序的源代码,则可以使用另一个小的帮助程序功能对其进行转换:
eitherToExcept :: forall e a. Aff (Either e a) -> ExceptT e Aff a
eitherToExcept action = either throwError pure <$> lift action
现在,您的main
函数可以完成ExceptT
monad中的所有工作,让它在后台传播错误,仅在最后使用runExceptT
将结果转换回到Either
:
main = launchAff_ $ either errorShow (liftEffect <<< logShow) $ runExceptT do
let searchTerm = "PureScript"
credentials <- eitherToExcept $ readConfig "./config/twitter_credentials.json"
tokenCredentials <- eitherToExcept $ getTokenCredentials credentials
results <- eitherToExcept $ showResults tokenCredentials searchTerm
pure $ "Response:" <> (show results.statuses)
P.S。因为我没有时间编译和验证代码,所以这里和那里可能会有一些错别字。