首先,请记住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)
在线尝试!