我尝试创建一个记录,其中一个字段的类型未导出,因为它使用的是智能构造函数。使用智能构造函数作为类型不起作用。
Not in scope: type variable `domain'
也许有一种语言扩展允许我这样做,或类似的东西?
将构造函数与智能构造函数一起导出将允许我解决此问题,但这反过来又创建了创建智能构造函数不允许的值的可能性。
我现在拥有的(非工作)代码:
import Domain (domain) -- Domain is not exported, and domain is a smart constructor for Domain
data Rec = Rec
{ dint :: domain Int -- what do I do here? I want it to be `Domain Int` but `Domain` isn't exported.
...
}
答案 0 :(得分:6)
这里的问题是类型构造函数的概念与数据构造函数之间的混淆。为简洁起见,我将用一个例子来说明差异。
data Foo a = Bar [a]
在上面的表达式中,Foo
是类型构造函数,Bar
是数据构造函数。关键区别在于Foo
是Haskell类型空间中的值,Bar
是其数据空间中的值。类型空间中的值不能用于数据空间,反之亦然。例如,编译器会在以下表达式中出错。
someVariable :: Bar Int
someVariable = Foo [15]
然而,下一个表达式完全有效。
someVariable :: Foo Int
someVariable = Bar [15]
此外,所有类型构造函数必须以大写字母开头。任何以小写字母开头的类型都将被视为类型变量,而不是类型构造函数(上面定义中的a
就是一个示例)。
智能构造函数的引入为这个问题增加了另一层,但要理解的关键是智能构造函数是数据构造函数,而不是类型构造函数。在Rec
的定义中,您尝试在domain
字段的类型声明中使用智能构造函数dint
。但是,因为domain
是数据构造函数而不是类型构造函数,并且它是小写的,所以Haskell编译器试图将domain
解释为类型变量的名称。因为您从未在domain
类型的定义中指定名为Rec
的变量,所以编译器引发了错误。
您实际上并不需要导出Domain
的数据构造函数来解决问题,只需要输入类型本身。这可以通过以下方式完成。
module Domain (
Domain(), domain,
...
) where
在导出定义中包含Domain()
告诉Haskell导出Domain
类型构造函数,但不导出任何数据构造函数。这样可以使用安全构造函数保留所需的安全性,并允许您正确定义类型。您现在可以在Rec
。
import Domain (Domain(), domain)
data Rec = Rec
{ dint :: Domain Int
...
}
有关详细信息,我强烈建议您阅读constructors和smart constructors上的HaskellWiki文章。