【发布时间】:2021-08-09 13:25:50
【问题描述】:
这可能是一个非常基本的 Haskell 问题,但让我们假设以下函数签名
-- helper functions
getWeatherInfo :: Day -> IO (Either WeatherException WeatherInfo)
craftQuery :: WeatherInfo -> Either QueryException ModelQuery
makePrediction :: ModelQuery -> IO (Either ModelException ModelResult)
将以上所有内容链接到一个 predict day 函数中的天真方法可能是:
predict :: Day -> IO (Maybe Prediction)
predict day = do
weather <- getWeatherInfo day
pure $ case weather of
Left ex -> do
log "could not get weather: " <> msg ex
Nothing
Right wi -> do
let query = craftQuery wi
case query of
Left ex -> do
log "could not craft query: " <> msg ex
Nothing
Right mq -> do
prediction <- makePrediction mq
case prediction of
Left ex -> do
log "could not make prediction: " <> msg ex
Nothing
Right p ->
Just p
在更命令式的语言中,可以执行以下操作:
def getWeatherInfo(day) -> Union[WeatherInfo, WeatherError]:
pass
def craftQuery(weather) -> Union[ModelQuery, QueryError]:
pass
def makePrediction(query) -> Union[ModelResult, ModelError]:
pass
def predict(day) -> Optional[ModelResult]:
weather = getWeatherInfo(day)
if isinstance((err := weather), WeatherError):
log(f"could not get weather: {err.msg}")
return None
query = craftQuery weather
if isinstance((err := query), QueryError):
log(f"could not craft query: {err.msg}")
return None
prediction = makePrediction query
if isinstance((err := prediction), ModelError):
log(f"could not make prediction: {err.msg}")
return None
return prediction
在许多方面可以说它的类型安全性和笨重程度较低,但也可以说是平淡得多。我可以看到主要区别在于,在 Python 中,我们可以(是否应该是另一回事)使用多个早期的 return 语句来在任何阶段停止流程。但这在 Haskell 中是不可用的(无论如何这看起来很不习惯,并且首先破坏了使用该语言的全部目的)。
然而,当处理一个接一个地链接连续的Either/Maybe 的相同逻辑时,是否有可能在 Haskell 中实现相同的“平坦度”?
-- 按照重复的建议进行编辑:
我可以看到另一个问题是如何相关的,但仅此而已 (相关)——它没有回答这里暴露的问题,即如何 展平 3 级嵌套案例。此外这个问题(这里) 以比另一种更通用的方式暴露问题, 这是非常特定于用例的。我想回答这个问题 (此处)将对社区的其他读者有益, 与其他人相比。
我知道对于经验丰富的 Haskeller 而言,这似乎是多么明显 “just use EitherT”听起来像是一个完全正确的答案,但是 这里的重点是,这个问题是从以下角度提出的 一个不是经验丰富的 Haskeller 的人,也是一个阅读过的人 再次说明 Monad 变压器有其局限性,也许是免费的 monad 或 Polysemy 或其他替代方案是最好的,等等。我猜 这将有助于整个社区拥有这个特定的 在这方面用不同的替代方案回答了这个问题,所以 新手 Haskeller 会发现自己的“迷失在翻译中”稍微少一些 当开始面对更复杂的代码库时。
【问题讨论】:
-
您可以使用
MaybeTmonad 转换器。 -
您可以用
MaybeTconstructor 将其包装在MaybeT中:这会将m (Maybe a)变成MaybeT m a。 -
EitherT也是如此:hackage.haskell.org/package/EitherT-0.2.0/docs/… -
@Jivan 我可以看到它看起来是这样的,但其他答案确实显示了如何摆脱单个
case。本质上,它展平了嵌套的“1 级”case。 Stack Overflow 上还有很多其他示例可以解决该问题:stackoverflow.com/q/33005903/126014、stackoverflow.com/q/52016330/126014、stackoverflow.com/q/50136713/126014
标签: haskell monads flatten maybe either