我有一个看起来像这样的构造函数:
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
显然,随着更多FilterTypes的添加,mkFilterParams
开始包含大量重复的代码。是否有更有效/可扩展的方式来实现这一点?我已经盯着它看了很长时间才能看到新的方式。
答案 0 :(得分:4)
我的另一个答案讨论了一个非常简单的解决方案,即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
捆绑在一起;例如也许你会有一个像这样的类型的函数:
filterMatches :: FilterType a -> a -> Record -> Bool
这样的捆绑甚至可能值得在其自己的存在类型中形式化:
data Filter where Filter :: FilterType a -> a -> Filter
答案 1 :(得分:3)
这样的事情怎么样?
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
,这可能会感觉最干净。