代码之家  ›  专栏  ›  技术社区  ›  jek339

制作可伸缩setter

  •  1
  • jek339  · 技术社区  · 6 年前

    我有一个类似这样的构造函数:

    data FilterType 
      = OwnerId
      | OwnerIdReportType
      | ReportTypeProductType
    
    filterParams = FilterParams
      { filterType :: FilterType
      , ownerId :: Maybe Id
      , productType :: Maybe ProductType
      , reportType :: Maybe ReportType
      }
    

    然后,当我设置过滤器参数时,我是这样做的,所以我只提取我需要的数据:

    defaultFilterParams =
      { filterType = OwnerId
      , ownerId = Nothing
      , productType = Nothing
      , reportType = Nothing
      }
    
    mkFilterParams :: FilterType -> Id -> IO FilterParams
    mkFilterParams OwnerId reportId = do
      ownerId <- getOwnerId reportId
      defaultFilterParams { ownerId }
    mkFilterParams ReportTypeProductType reportId = do
      reportType <- getReportType reportId
      productType <- getProductType reportId
      defaultFilterParams
        { filterType = ReportTypeProductType
        , productType
        , reportType
        }
    -- etc
    

    显然,随着过滤器类型的增加, mkFilterParams 开始包含大量重复代码。是否有更高效/可扩展的方法来实现这一点?我盯着它看了太久,以至于看不到新的方法。

    2 回复  |  直到 6 年前
        1
  •  4
  •   Daniel Wagner    6 年前

    我的另一个答案讨论了一个非常简单的解决方案,即Haskell2010;这具有易于理解和使用的优点。但它确实有一个有趣的构造函数名称重复。在这个回答中,我将简要描述如何使用GADT来避免这个问题。GADT为我们提供了一种连接术语构造函数和类型级别的方法。因此:

    data FilterType a where
        Owner :: FilterType Id
        Product :: FilterType ProductType
        ReportProduct :: FilterType (ReportType, ProductType)
    

    然后,我们可以为每种类型的过滤器返回不同的信息:

    mkFilter :: FilterType a -> ReportId -> IO a
    mkFilter Owner = getOwnerId
    mkFilter Product = getProductId
    mkFilter ReportProduct = \reportId -> (,) <$> getReportType reportId
                                              <*> getProductType reportId
    

    假设要使用其中一个,我们需要将 FilterType a a ;e、 g.也许您会有一个类型如下的函数:

    filterMatches :: FilterType a -> a -> Record -> Bool
    

    这样的捆绑包甚至值得在其自身的存在类型中进行形式化:

    data Filter where Filter :: FilterType a -> a -> Filter
    
        2
  •  3
  •   Daniel Wagner    6 年前

    像这样的怎么样?

    data FilterType = OwnerT | ProductT | ReportProductT
    data Filter
        = Owner Id
        | Product ProductType
        | ReportProduct ReportType ProductType
    
    mkFilter :: FilterType -> ReportId -> IO Filter
    mkFilter ty reportId = case ty of
        OwnerT -> Owner <$> getOwnerId reportId
        ProductT -> Product <$> getProductType reportId
        ReportProductT -> ReportProduct <$> getReportType reportId
                                        <*> getProductType reportId
    

    如果重复 reportId 困扰您的是,您可以考虑使用以下类型:

    mkFilter :: FilterType -> ReaderT ReportId IO Filter
    

    如果您还修改了 getOwnerId ,则, getReportType getProductType 使用 ReaderT