没有数据构造函数的数据声明.可以实例化吗?为什么编译?

mhe*_*rzl 5 haskell types

读了我的一本Haskell书,我看到了这句话:

数据声明总是创建一个新的类型构造函数,但可能会也可能不会创建新的数据构造函数.

一个人应该能够声明没有数据构造函数的数据类型,这听起来很奇怪,因为似乎人们永远无法实例化该类型.所以我试了一下.以下数据声明编译时没有错误.

data B = String
Run Code Online (Sandbox Code Playgroud)

如何创建这种类型的实例?可能吗?我似乎无法找到办法.

我想可能会自动创建一个名称与类型构造函数匹配的数据构造函数,但似乎不是这种情况,如尝试B在范围内使用声明作为数据构造函数所导致的错误所示.

Prelude> data B = String deriving Show
Prelude> B

<interactive>:129:1: error: Data constructor not in scope: B
Run Code Online (Sandbox Code Playgroud)

如果永远不能实例化类型,为什么允许编译此数据声明? 尽管没有已知的实际应用,是否仅仅出于某种正式理由允许?


我也想知道我的书中关于没有构造函数的数据类型的陈述是否可能是指通过typenewtype关键字而不是by 来声明的类型data.

  • 在这种type情况下,类型同义词显然不使用数据构造函数,如下所示.

    Prelude> type B = String
    Prelude>
    
    Run Code Online (Sandbox Code Playgroud)

    诸如此类型的同义词可以由它们被设置为的类型的构造函数实例化.但我不相信这是我的书所指的那样,因为类型同义词似乎没有像现有类型定义新别名那样声明新数据类型.

  • 在这种newtype情况下,似乎无法创建没有数据构造函数的类型,如以下错误所示.

    Prelude> newtype B = String
    
    <interactive>:132:13: error:
        • The constructor of a newtype must have exactly one field
            but ‘String’ has none
        • In the definition of data constructor ‘String’
          In the newtype declaration for ‘B’
    
    Run Code Online (Sandbox Code Playgroud)

type并且newtype看起来并不像本书所指的那样,这让我回到原来的问题:为什么可以在data没有数据构造函数的情况下声明一个类型?

Ale*_*lec 11

如何创建这种类型的实例?

你的书中的陈述是正确的,但你的例子不是.data B = String定义一个类型构造函数 B和一个数据构造函数 String,两者都不带参数.请注意,String您定义的是值命名空间,因此与通常的String类型构造函数不同.

ghci> data B = String
ghci> x = String
ghci> :t x
x :: B
Run Code Online (Sandbox Code Playgroud)

但是,这里是没有数据构造函数的数据定义的示例(因此无法实例化).

ghci> data B
Run Code Online (Sandbox Code Playgroud)

现在,我有一个新的类型构造函数B,但没有数据构造函数来生成类型的值B.实际上,在Haskell中声明了这样的数据类型base:它被称为Void:

ghci> import Data.Void
ghci> :i Void
data Void   -- Defined in ‘Data.Void’
Run Code Online (Sandbox Code Playgroud)

如果永远不能实例化类型,为什么允许编译此数据声明?

能够拥有无人居住的类型在少数地方变得有用.我现在能想到的例子大多是将类型参数这样的类型传递给另一个类型的构造函数.另一个实际用例是在流式库中conduit.

有一个ConduitM i o m r类型构造函数,其中:i是输入流元素o的类型,输出流元素的类型,m执行操作的monad,r是最后生成的最终结果.

然后,它定义了一个Sinkas

type Sink i m r = ConduitM i Void m r
Run Code Online (Sandbox Code Playgroud)

因为a Sink永远不应该输出任何值.Void是一个编译时保证,Sink 不能输出任何(非底部)值.

很像Identity,Void与其他抽象结合使用最为有用.

...类型同义词显然不使用数据构造函数

是的,但他们也没有定义类型构造函数.同义词只是一些表面级别的便捷重命名.在引擎盖下,没有新的定义.

在newtype情况下,似乎无法创建没有数据构造函数的类型,如以下错误所示.

我建议你查找是什么newtype.整个问题newtype是围绕现有类型提供零成本包装.这意味着你有一个且只有一个构造函数只接受一个参数(包装的值).在编译时,包装和解包操作变为NOP.

  • @mherzl的确,您定义了一个构造函数`String`,它与该类型具有相同的名称,但与它完全无关.函数类型`a - > b`也没有构造函数:它们的值是使用lambdas`\x - > ...`(或命名函数定义)而不是构造函数引入的,并且通过应用程序`fx`而不是模式匹配来消除. (2认同)