改进代码:删除嵌套容器和代码重复

时间:2018-10-29 00:12:19

标签: purescript

我正在寻找有关编写惯用的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。您将如何编写此代码以使其更具可读性并可能消除代码重复?

1 个答案:

答案 0 :(得分:1)

您可以将辅助函数从Aff (Either String a)返回到ExceptT String Aff aExceptT是一个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。因为我没有时间编译和验证代码,所以这里和那里可能会有一些错别字。