haskell - 作为 Maybe 部分的结果创建 Maybe 类型的最佳方法
问题描述
我有一个Request
类型:
data Request =
Request {
reqType :: RequestType,
path :: String,
options :: [(String, String)]
} deriving Show
我正在解析它(来自原始HTTP
请求),如下所示:
parseRawRequest :: String -> Request
parseRawRequest rawReq =
Request {
reqType = parseRawRequestType rawReq,
path = parseRawRequestPath rawReq,
options = parseRawRequestOps rawReq
}
parseRawRequestType
现在,对, parseRawRequestPath
(etc)的调用可能会失败。为了使我的代码更具弹性,我将它们的类型签名从:
parseRawRequestType :: String -> RequestType
至
parseRawRequestType :: String -> Maybe RequestType
但是parseRawRequest
变成的最好方法是Maybe Request
什么?我是否必须手动检查每个组件(reqType
, path
, options
)Nothing
,或者是否有我遗漏的不同方式?
必须有一种方法可以以某种方式组合对象创建和Nothing
检查!
我写了以下内容,但感觉很乱且不理想:(未经测试)
parseRawRequest :: String -> Maybe Request
parseRawRequest rawReq
| Nothing `elem` [reqType, path, options] = Nothing
| otherwise =
Just Request { reqType=reqType, path=path, options=options }
where reqType = parseRawRequestType rawReq
path = parseRawRequestPath rawReq
options = parseRawRequestOps rawReq
干杯。
解决方案
这正是 Applicative Functors ( Control.Applicative
) 所代表的模式。Applicatives 就像普通的 Functors,但是有两个额外的操作:
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
pure
允许您将任何值放入应用程序中,这意味着对于任何应用程序,您都可以编写fmap
as fmap f x = pure f <*> x
。
在这种情况下,有趣的运算符是<*>
。这个想法是,如果你在函子“内部”有一个函数,你可以将它应用于函子中的另一个值。如果你这样做Request <$> (_ :: Maybe RequestType)
,你会得到一些类型的东西Maybe (String -> [(String, String)] -> Request)
。然后,<*>
操作员将让您将其应用到Maybe String
要获取的类型上Maybe [(String, String)] -> Request)
,依此类推。
例子:
data RequestType
data Request =
Request { reqType :: RequestType, path :: String, options :: [(String, String)] }
parseRawRequestType :: String -> Maybe RequestType
parseRawRequestType = undefined
parseRawRequestPath :: String -> Maybe String
parseRawRequestPath = undefined
parseRawRequestOps :: String -> Maybe [(String, String)]
parseRawRequestOps = undefined
parseRawRequest :: String -> Maybe Request
parseRawRequest rawReq = Request <$> parseRawRequestType rawReq
<*> parseRawRequestPath rawReq
<*> parseRawRequestOps rawReq
但是请注意,所应用的函数必须具有类型f (a -> b)
,而不是a -> m b
常见的一元绑定运算符。在有效的上下文中,您可以将其视为<*>
一种无需检查中间结果即可对效果进行排序的方法,而>>=
为您提供了更多功能(注意:Applicative Functor 和 Monad 的功能之间的本质区别是 function join :: m (m a) -> m a
。可以你认为如何获得和>>=
?)。但是,Applicative 是一个更通用的接口,这意味着您可以在更多情况下使用它们,并且在分析/优化方面有时它们可以具有很好的属性。似乎对 Applicatives 与 Monads 有一个不错的概述,以及何时您可能想在此处使用 Applicatives<*>
join
.
推荐阅读
- python - 在 python 单元测试中集成模拟和补丁
- javascript - 我怎样才能使它成为一个循环?
- vuex - Vuex 存储 API 数据完整性
- ruby - 如何在字符串中搜索最长的单词
- powerbi - power bi,使用来自多个租户的数据集构建数据模型
- ios - SwiftUI 在自定义 TextField 上切换活动/非活动线条颜色
- python - Python:从 seaborn kdeplot 获取 FWHM
- mysql - 存储过程是否会增加应用程序的性能以及使用它们的理想场景?
- colors - 如何在 utils.MultiSelectSpinner 中更改我的默认选定文本颜色
- javascript - CefShap - 如何通过 C# 将双击注入网页?