【问题标题】:Best way to create Maybe type as the result of Maybe parts作为 Maybe 部分的结果创建 Maybe 类型的最佳方法
【发布时间】:2018-07-14 00:21:56
【问题描述】:

我有一个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 的最佳方法是什么?我是否必须手动检查 Nothing 的每个组件(reqType、path、options),还是我遗漏了其他方法?

一定有办法以某种方式组合对象创建和Nothing-checking!

我写了以下,但感觉很乱,不理想:(未经测试)

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

干杯。

【问题讨论】:

  • 问题标题中的“sum type”一词有点令人困惑,因为您的 Request 类型是多个 Maybe 字段的 product,而不是它们的总和。
  • 你是 100% 正确的。已编辑。

标签: haskell monads maybe


【解决方案1】:

这正是 Applicative Functors (Control.Applicative) 所代表的模式。 Applicative 与常规 Functor 类似,但有两个额外的操作:

pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b

pure 允许您将任何值放入应用程序,这意味着对于任何应用程序,您都可以将fmap 写为fmap f x = pure f &lt;*&gt; x。

本例中有趣的运算符是&lt;*&gt;。这个想法是,如果你在函子“内部”有一个函数,你可以将它应用于函子中的另一个值。如果你做Request &lt;$&gt; (_ :: Maybe RequestType),你会得到Maybe (String -&gt; [(String, String)] -&gt; Request) 类型的东西。然后,&lt;*&gt; 运算符将让您将其应用于 Maybe String 类型的东西以获取 Maybe [(String, String)] -&gt; 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 -&gt; b) 类型,而不是常见的一元绑定运算符的 a -&gt; m b。在有效的上下文中,您可以将其视为&lt;*&gt; 提供了一种无需检查中间结果即可对效果进行排序的方法,而&gt;&gt;= 为您提供了更多功能(注意:Applicative Functor 和Monad是一个函数join :: m (m a) -&gt; m a。你能想到如何用&lt;*&gt;和join获得&gt;&gt;=吗?)。但是,Applicatives 是一个更通用的接口,这意味着您可以在更多情况下使用它们,并且在分析/优化方面有时它们可​​以具有很好的属性。似乎对 Applicatives 与 Monads 有一个不错的概述,以及何时您可能想要使用 Applicatives here。

【讨论】:

    猜你喜欢
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2022-01-12
    • 2018-10-17
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    相关资源
    最近更新 更多