编写模式同义词以隐藏构造函数



考虑以下内容:

module MyModule (
A(FortyTwo), -- Note we don't expose PrivateA
B(P) -- Nor PrivateB
) where
pattern FortyTwo = A 42
newtype A = PrivateA Int
data B = PrivateB Int Int
pattern P :: Int -> A -> B

如何编写模式P

基本上我可以说:

f :: B -> String
f (P 2 FortyTwo) = "The meaning of life"

也就是说,能够在不直接在定义私有构造函数PrivateAPrivateB的模块外部引用它们的情况下进行模式匹配。

首先,请记住newtype只能与具有单个参数的数据构造函数一起使用,因此A可以是newtype,但B不能。

data B = PrivateB Int Int

现在,您为FortyTwo使用的模式语法称为隐式双向。也就是说,

pattern FortyTwo :: A
pattern FortyTwo = PrivateA 42

我们使用=,真正意味着平等。上面写着";我可以用FortyTwo来构造A,如果我有A,我可以使用FortyTwo来对其进行模式匹配";。这是最简单的形式,在";简单的";论点指向良好的情况。

但现实世界并没有那么简单。因此GHC为我们提供了一种扩展语法,称为显式双向模式。简而言之,我们可以明确指定我们希望表达式在模式上下文中的行为方式,以及我们希望它在表达式上下文中的表现方式。编译器不会(也不能(检查这两个表达式作为一对是否有内聚意义,所以我们可以用它来做一些像这样的废话

pattern Nonsense :: Int -> Int
pattern Nonsense n <- n where
Nonsense _ = 42

这定义了Nonsense,使得let Nonsense x = Nonsense 0 in x返回42

但是您的用例听起来完全合理,所以我们可以用明确的双向模式来定义它。

我们还需要完成一个小部分,那就是视图模式。视图模式是一种模式(因此,我们在模式匹配中使用它(,它实际上只是伪装的函数调用。简而言之,以下是大致等效的

let y = f x in y
let (f -> y) = x in y

它实际上只是将函数调用移动到等号的另一边,这在某些情况下可以方便地编写简洁的代码。它在定义模式同义词时也很有用。

pattern P :: Int -> A -> B
pattern P n a <- PrivateB n (PrivateA -> a) where
P n (PrivateA a) = PrivateB n a

当然,第一行是类型声明。第二行说";当我看到形式为P n a的模式时,假装它说PrivateB n (PrivateA -> a)";。最后一行写着";当我看到一个说P n (PrivateA a)的表达式时,构建一个PrivateB n a";。这定义了一个Haskell函数(该函数是穷举的,因为A只有一个构造函数,我们已经处理过了(。

完整的可运行示例:

{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
module Main where
pattern FortyTwo :: A
pattern FortyTwo = PrivateA 42
newtype A = PrivateA Int
data B = PrivateB Int Int
pattern P :: Int -> A -> B
pattern P n a <- PrivateB n (PrivateA -> a) where
P n (PrivateA a) = PrivateB n a
f :: B -> String
f (P 2 FortyTwo) = "The meaning of life"
f _ = "Nope :("
main :: IO ()
main = do
putStrLn $ f (PrivateB 2 42)
putStrLn $ f (PrivateB 2 43)

在线试用!

最新更新