我最近试图将一个ADT重构 - 其中构造函数的数量已经组合增长 - 转化为向后兼容的集合表示:
data Tag = TagFoo | TagBar !Text | TagBaz !Int ... -- many more
deriving (Eq, Generic, Ord, Show)
newtype Label = Label (HashSet Tag)
deriving (Eq, Generic, Show)
为此,我定义了几种模式同义词:
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
pattern Foo :: Label
pattern Foo <- Tags [TagFoo] where
Foo = Label $ HashSet.singleton TagFoo
-- (let's say a lone TagBar is invalid)
pattern FooWithBar :: Text -> Label
pattern FooWithBar b <- Tags [TagFoo, TagBar b] where
FooWithBar b = Label $ HashSet.fromList [TagFoo, TagBar b]
将Tags
模式定义为:
pattern Tags :: [Tag] -> Label
pattern Tags ts <- ((\(Label ts') -> sort $ HashSet.toList ts') -> ts)
不幸的是,这种形式容易出错,因为它要求用户在正确的[Tag]
中提供Ord
列表。否则,Tags [TagBar "x", TagFoo]
之类的模式将与Label $ HashSet.fromList [TagBar "x", TagFoo]
不匹配。 (不是sort
更糟糕的是,因为那时标签的顺序是任意的。)
理想情况下,Haskell(或无序容器?)将提供一种模式匹配HashSet
s元素的方法。但另一种方法是将ts
模式的Tags ts
参数映射到HashSet.fromList
,然后比较结果集:
pattern Tags ts <- ((\(Label ts') -> ts' == HashSet.fromList ts) -> True)
然而,这是不可能的,因为视图模式函数不能使用模式同义词的参数。但是试图在视图函数之外进行转换:
pattern Tags ts <- ((\(Label ts') -> ts') -> HashSet.fromList ts == ts')
也是不可能的,因为->
之后的部分是一个模式,不允许函数应用。
是否有其他方法可以定义允许这种匹配的模式同义词?
答案 0 :(得分:1)
Tags
真的需要成为一种模式吗?简单地提供功能有什么问题:
toLabel :: [Tags] -> Label
让用户使用警卫:
someFunction lab | lab == toLabel [TagFoo, TagBar "bar"] = ...