作为 Maybe 部分的结果创建 Maybe 类型的最佳方法
Best way to create Maybe type as the result of Maybe parts
我有一个 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
(等)的调用可能会失败。为了使我的代码更具弹性,我更改了它们的类型签名:
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
写为 fmap f x = pure f <*> x
.
本例中有趣的运算符是 <*>
。这个想法是,如果你有一个函数 "inside" 仿函数,你可以将它应用到仿函数中的另一个值。如果你这样做 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 and a Monad is a function join :: m (m a) -> m a
. 你能想到如何用 <*>
和 join
获得 >>=
吗?)。但是,Applicatives 是一个更通用的接口,这意味着您可以在更多情况下使用它们,并且当涉及到 analysis/optimization 时,它们有时可以具有不错的属性。似乎有一个关于 Applicatives 与 Monads 以及您何时可能想要使用 Applicatives here.
的不错的概述
我有一个 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
(等)的调用可能会失败。为了使我的代码更具弹性,我更改了它们的类型签名:
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
写为 fmap f x = pure f <*> x
.
本例中有趣的运算符是 <*>
。这个想法是,如果你有一个函数 "inside" 仿函数,你可以将它应用到仿函数中的另一个值。如果你这样做 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 and a Monad is a function join :: m (m a) -> m a
. 你能想到如何用 <*>
和 join
获得 >>=
吗?)。但是,Applicatives 是一个更通用的接口,这意味着您可以在更多情况下使用它们,并且当涉及到 analysis/optimization 时,它们有时可以具有不错的属性。似乎有一个关于 Applicatives 与 Monads 以及您何时可能想要使用 Applicatives here.