1

考虑以下:

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

4

1 回答 1

4

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

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 x42

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

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

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)

在线尝试!

于 2021-05-15T03:57:19.010 回答