如何将带字段的Haskell数据类型重构为带标记的记录并集?

nue99wik  于 2022-11-14  发布在  其他
关注(0)|答案(1)|浏览(113)

在一个大的代码库中,我想将一个具有带字段的构造函数的数据类型重构为一个简单的带标记的记录联合体,主要是为了更好地支持-Wincomplete-record-updates。重要的是,我不需要更改此数据类型上的任何模式匹配,所以我尝试使用记录模式同义词。下面是一个简化版本:

{-# LANGUAGE PatternSynonyms #-}

{-# OPTIONS_GHC -Werror=incomplete-patterns #-}

-- Consider some data type with fields:

data D'
  = A'
  | B' { b'Int :: Int, b'String :: String }

foo' :: D' -> String
foo' A'{} = "A"
foo' B'{b'String = x} = x

-- I'd like to factor out a record containing the fields
-- without changing foo'.

data D
  = A
  | BTag BRec

data BRec = BRec { _bInt :: Int, _bString :: String }

pattern B :: Int -> String -> D
pattern B{ bInt, bString } = BTag (BRec bInt bString)

foo :: D -> String
foo A{} = "A"
foo B{bString = x} = x

-- I get this error:
--     Pattern match(es) are non-exhaustive
--     In an equation for ‘foo’: Patterns of type ‘D’ not matched: BTag _
--    |
-- 28 | foo A{} = "A"
--    | ^^^^^^^^^^^^^...

为什么我会得到一个模式覆盖错误?我使用的模式同义词正确吗?如何得到我想要的重构?

nimxete2

nimxete21#

您应该使用COMPLETE pragma:

{-# COMPLETE A, B #-}

这可以放在文件的任何地方,但是作为一个组织性的东西,把它放在模式定义中是有用的。值得注意的是,如果有多个不同的集合覆盖了所有的可能性,你可以为同一个类型定义多个COMPLETE编译指示。例如,Data.Sequence.Seq有两个:

{-# COMPLETE (:<|), Empty #-}
{-# COMPLETE (:|>), Empty #-}

这使您可以灵活地提供重叠的同义词集,这些同义词集可以通过不同的方式完成。
至于为什么有必要这样做的理由,this article on the GHC wiki给出了一个很好的论证:
模式同义词是一种 * 抽象 * 的方法,如果穷举检查器可以查看定义,那么P的实现将泄漏到错误消息中。我们希望用户能够在消费者不注意的情况下,用模式同义词替换真正的数据构造器。

相关问题