创建Maybe类型作为Maybe部分的结果的最佳方法

haz*_*haz 1 monads haskell maybe

我有一个Request类型:

data Request =
  Request {
     reqType :: RequestType,
     path    :: String,
     options :: [(String, String)]
  } deriving Show
Run Code Online (Sandbox Code Playgroud)

我正在解析它(来自原始HTTP请求),如下所示:

parseRawRequest :: String -> Request
parseRawRequest rawReq =
    Request {
        reqType = parseRawRequestType rawReq,
        path    = parseRawRequestPath rawReq,
        options = parseRawRequestOps  rawReq
  }
Run Code Online (Sandbox Code Playgroud)

现在,在两个电话parseRawRequestType,parseRawRequestPath(等)可能会失败.为了使我的代码更具弹性,我改变了他们的类型签名:

parseRawRequestType :: String -> RequestType
Run Code Online (Sandbox Code Playgroud)

parseRawRequestType :: String -> Maybe RequestType
Run Code Online (Sandbox Code Playgroud)

但是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
Run Code Online (Sandbox Code Playgroud)

干杯.

Pet*_*don 5

这正是Applicative Functors(Control.Applicative)所代表的模式.Applicative就像普通的Functors,但有两个额外的操作:

pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

pure允许你将任何值放入应用程序中,这意味着对于任何应用程序,你可以写一个fmapas 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
Run Code Online (Sandbox Code Playgroud)

但请注意,应用的函数必须具有类型f (a -> b)而不是a -> m b公共monadic绑定运算符.在有效的环境中,您可以将此视为<*>在不检查中间结果的情况下提供序列效果的方法,而>>=为您提供更多的功能(注意:Applicative Functor和Monad的功能之间的本质区别是一个功能join :: m (m a) -> m a.你认为如何获得>>=<*>join?).但是,Applicatives是一个更通用的界面,这意味着您可以在更多情况下使用它们,并且在分析/优化时它们有时可以具有良好的属性.似乎有一个很好的概述Applicatives vs Monads,当你可能想在这里使用Applicatives时.