Haskell 具有可见性约束的构造函数或工厂方法中的守卫

Guards in a Haskell constructor or factory methods with visibility constraints

我对 Haskell 比较陌生,在进行实验时我遇到了一个问题,看起来我想在构造函数中使用守卫。我的真实实验有点复杂,但归结为

data X a = Zero
   | a==0 = Zero
   | otherwise = Some a

我知道这是错误的语法,特别是因为 a 是类型变量,而不是值变量,所以 a==0 无论如何都是无意义的。

背景是值ZeroSome 0在所有方面都应该相等,我什至可以说相同。事实上,我从来不想构造一个 Some 0,这应该被禁止,或者像上面起草的那样,在构造函数中已经悄悄地转换为 Zero

我认为 Haskell 中有一个常见的成语。在 Java 中,我只会创建两个构造函数 private 并提供工厂方法来创建一个或另一个 some(0) 返回 Zero 而不是 Some(0)。 Haskell 你也是这样吗?

编辑:好的,谷歌搜索更多我终于找到了 Haskell Factory Function entry,这似乎是答案的一部分。

要将构造函数设为私有,只需不要将其导出即可,例如:

module Foo (Bar, mkBar) where

data Bar = Zero | Some a | ...

mkBar :: Int -> Bar  -- exported smart constructor
mkBar a = ...

库的用户可以使用 mkBar 函数创建 Bar,但不能使用 ZeroSome ...

关键是导出列表中的Bar。如果您指定:

module Foo (Bar(..), ...) where

那么 Bar 的构造函数也将被导出。

In Java I would just make the two constructors private and provide factory methods to create either one or the other

介绍了如何通过在 Haskell 中做类似的事情来解决您的问题。我只想指出一个容易混淆的问题。在你的伪Haskell片段中...

data X a = Zero
   | a==0 = Zero
   | otherwise = Some a

...您正在将 data X a 中的 a 视为 X 值的构造函数的参数,以便您能够例如测试是否a == 0。但是,a 是一个 type 参数。它可以被不同的类型代替,所以X a实际上对应了X IntX DoubleX String等一系列类型。这与您如何使用 Java 泛型来参数化 class 非常相似,如在 class X<A>.

等声明中

使用通用术语,X 是一个 类型构造函数 ,它接受一个类型并生成另一个类型(例如从 IntX Int). value(或data)构造函数是您在等号后指定的构造函数。如您所知,您可以将它们用作常规值,它们可能是也可能不是函数,具体取决于它们是否接受参数。所以如果我们这样做...

GHCi> data X a = Zero | Some a

... 那么 Zero 是一个 X a 类型的值,用于任意 a...

GHCi> :t Zero
Zero :: X a

... 而 Some 是从 aX a 的函数:

GHCi> :t Some
Some :: a -> X a

至于类型构造函数X,我们不说它的类型,而是说它的种类。类型和类型构造函数的种类类似于值的类型。 GHCi 中的 :kind(或 :k)命令允许您检查类型:

GHCi> :k Int
Int :: *
GHCi> :k X
X :: * -> *

* -> * 表示 X 取一个类型并产生一个类型,如上所述。