改进代码:删除嵌套的 Eithers 和代码重复
Improve Code: Remove Nested Eithers and Code Duplication
我正在寻找有关编写惯用 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
语句,我调用了3次errorShow
。您将如何编写此代码以使其更具可读性并可能消除代码重复?
您可以将辅助函数从返回 Aff (Either String a)
转换为 ExceptT String Aff a
。 ExceptT
是一个 monad 转换器,它用 Either e a
代替值,这意味着您编译的代码看起来大致相同。但在源代码级别,您可以忽略错误直到最后,从而获得可读性并减少重复。
如果你控制了辅助函数的来源,直接重写它们:不返回Left
,使用throwError
,不返回Right
,使用[=19] =].
另一方面,如果您无法控制助手的源代码,您可以使用另一个小的助手函数来转换它们:
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。由于我没有时间编译和验证代码,可能会有一些错别字。
我正在寻找有关编写惯用 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
语句,我调用了3次errorShow
。您将如何编写此代码以使其更具可读性并可能消除代码重复?
您可以将辅助函数从返回 Aff (Either String a)
转换为 ExceptT String Aff a
。 ExceptT
是一个 monad 转换器,它用 Either e a
代替值,这意味着您编译的代码看起来大致相同。但在源代码级别,您可以忽略错误直到最后,从而获得可读性并减少重复。
如果你控制了辅助函数的来源,直接重写它们:不返回Left
,使用throwError
,不返回Right
,使用[=19] =].
另一方面,如果您无法控制助手的源代码,您可以使用另一个小的助手函数来转换它们:
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。由于我没有时间编译和验证代码,可能会有一些错别字。