考虑以下内容:
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"
也就是说,能够在不直接在定义私有构造函数PrivateA
和PrivateB
的模块外部引用它们的情况下进行模式匹配。
首先,请记住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)
在线试用!