有没有一种特性(模式同义词?)可以把一个数据类型变成用于构造另一个数据类型的语法糖?
假设我在我的库里定义了这个数据类型
data Foo = Foo (String, String, String, String)
并且我暴露了一堆可供使用的东西,但客户端实际上只需要构造一个 Foo 并将其传递给我定义的API。其他的什么都不用。
现在说,为了给客户端提供更多的灵活性,我想出了一个更好的数据类型
data Bar = Bar (Bool -> Int -> Bool -> String)
显然我不想搞坏现有的调用点,我可以通过把旧的 Foo 与新的 Bar 一起保留来确保这一点,客户端在寻找更多灵活性时应该能发现 Bar。
然而,由于一些无关的原因,同时拥有 Foo 和 Bar 有点烦人(例如,因为我必须在多个地方写它们)。
所以,最近接触到 pattern synonyms,我在想是否可以把 Foo 转化为创建 Bar 的零成本语法糖。¹
不知为何,感觉我需要 Pattern synonyms 功能的“向后”部分,意思是
- pattern synonyms的三种形式总是提供对数据类型进行模式匹配的另一种方式,
- 但只有其中两种给出构造一个值的另一种方式,
但在我的用例中,我并不需要第一点,但确实需要第二点。
确实,我希望来自假设的语法糖机器能够把任何出现的
Foo abcd
变成
let (a, b, c, d) = abcd
in Bar (\b1 i b2 -> "function of a, b, c, d, b1, i, b2 hardcoded in the library")
但这在上面的第一点中是做不到的。
¹ 当我写这个问题时,我在想一种做法是把 Foo 构造器从 Foo 类型复制粘贴到 Bar 类型,并删除 Foo 类型。可以说这不是我在寻找的解决方案。我猜这不会是零成本的,因为我得把所有接受 Bar 的函数改为通过对 Bar 与 Foo 构造的值进行模式匹配来分支,对吗?
解决方案
现有技术下,最好的做法可能就是把模式匹配转换为运行时错误,而不是编译时错误。就像这样:
data Bar = Bar (Bool -> Int -> Bool -> String)
pattern Foo :: HasCallStack => String -> Bar
pattern Foo s <- (error "Foo is for construction only" -> s)
where Foo s = Bar (\_ _ _ -> s)
在ghci中试试:
> let Foo x = Bar mempty in length x
*** Exception: Foo is for construction only
CallStack (from HasCallStack):
error, called at test.hs:9:19 in main:Main
Foo, called at <interactive>:1:5 in interactive:Ghci1
> let Bar f = Foo "x" in f True 3 False
"x"
你也可能需要 type Foo = Bar,以防你的现有调用方在类型签名中提到 Foo。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。