【问题标题】:Data declaration with no data constructor. Can it be instantiated? Why does it compile?没有数据构造函数的数据声明。可以实例化吗?为什么会编译?
【发布时间】:2017-07-29 05:25:15
【问题描述】:

阅读我的一本 Haskell 书籍,我遇到了这样一句话:

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

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

data B = String

如何创建这种类型的实例?有可能吗?我似乎找不到方法。

我认为可能会自动创建名称与类型构造函数匹配的数据构造函数,但似乎并非如此,如尝试使用 B 作为带有声明的数据构造函数所导致的错误所示范围内。

Prelude> data B = String deriving Show
Prelude> B

<interactive>:129:1: error: Data constructor not in scope: B

如果该类型永远无法实例化,为什么允许编译此数据声明? 尽管没有已知的实际应用,但是否仅出于某种形式的原因才允许?


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

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

    Prelude> type B = String
    Prelude>
    

    诸如此类的类型同义词可以由 他们设置的类型。但我不相信这是我的 本书所指的类型同义词似乎并没有像声明新数据类型那样多 只需为现有类型定义一个新别名。

  • 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’
    

typenewtype 似乎不是本书所指的内容,这让我回到了我最初的问题:为什么可以在没有数据构造函数的情况下使用 data 声明一个类型?

【问题讨论】:

    标签: haskell types


    【解决方案1】:

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

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

    ghci> data B = String
    ghci> x = String
    ghci> :t x
    x :: B
    

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

    ghci> data B
    

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

    ghci> import Data.Void
    ghci> :i Void
    data Void   -- Defined in ‘Data.Void’
    

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

    事实证明,能够拥有无人居住的类型在少数地方很有用。我现在能想到的例子大多是把这样一个类型作为类型参数传递给另一个类型构造函数。一个更实际的用例是在像conduit 这样的流式库中。

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

    然后,它将Sink定义为

    type Sink i m r = ConduitM i Void m r
    

    因为Sink 不应该输出任何值。 Void 是编译时保证Sink 不能输出任何(非底部)值。

    Identity 非常相似,Void 与其他抽象结合起来最有用。

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

    是的,但它们也没有定义类型构造函数。同义词只是一些表面级的方便重命名。在引擎盖下,没有定义任何新内容。

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

    我建议你查一下newtype 的用途。 newtype 的全部意义在于提供一个围绕 现有 类型的零成本包装器。这意味着您有一个且完全是一个构造函数,带有一个且完全是一个参数(包装值)。在编译时,包装和展开操作变成 NOP。

    【讨论】:

    • 啊,我看到我的示例是如何使用String 作为B 的数据构造函数,而不是作为String 的类型。
    • 我注意到函数的类型构造函数(-&gt;)Void 一样是另一个没有关联数据构造函数的类型构造函数。
    • @mherzl 实际上,您定义了一个构造函数String,它与类型同名,但与它完全无关。函数类型 a -&gt; b 也没有构造函数:它们的值是使用 lambdas \ x -&gt; ...(或命名函数定义)而不是构造函数引入的,并由应用程序 f x 而不是模式匹配消除。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多